zoukankan      html  css  js  c++  java
  • 暑期集训第二天(6-23)题解及总结

    今天已经是集训的第二天了,今天的考试主要考了区间DP,其实这一段我并不很熟。

    其实这是我第二遍写这篇博客了,原来那篇被某邹姓少年弄没了,我只好再写一遍了。

    PART ONE:DP复习二:

     其实这道题在上午的时候我写出来了。只是我用的是暴搜,可能数据比较水就让我过了,但是其实这道题的正解我是不会写的,于是就在这里补一下吧,其实我感觉暴搜在DP这里的应用领域还是挺广的,包括昨天和今天好多题我都是用暴搜做的,还过了不少。但是从今天下午开始暴搜的应用就不怎么广了,好几道题都是只能得到一半左右的分数

    方法一: 暴搜,用dfs的三个变量分别表示当前所处理到的位数,当前所分成的段数量及此时的乘积,暴搜的思路挺好想的,就是看数据,特别容易超时,所以最好慎用。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 const int N=5e1+10;
     5 #define int long long
     6 using namespace std;
     7 int len,a[N],b[N],pos[N],dp[N][N],score[N][N],g[N][N];
     8 int ans2[N],t[N],Max=-1;
     9 void dfs(int now,int m,int ans){
    10     if(now>len+1 || m<0) return;
    11     if(now==len+1&&m==0){
    12         if(ans>Max){
    13             Max=ans;
    14             for(int i=0;i<=t[0];++i)
    15                 ans2[i]=t[i];
    16         }
    17     }
    18     for(int i=now;i<=len;++i){
    19         t[++t[0]]=score[now][i];
    20         dfs(i+1,m-1,ans*score[now][i]);
    21         t[0]--;
    22     }
    23 }
    24 void Solve(){
    25     memset(dp,0,sizeof(dp));
    26     memset(score,0,sizeof(score));
    27     memset(g,0,sizeof(g));
    28     memset(t,0,sizeof(t));
    29     Max=-1;
    30     len=0;
    31     int num,m;
    32     scanf("%lld%lld",&num,&m);
    33     while(num){
    34         b[++len]=num%10;
    35         num/=10;
    36     }
    37     for(int i=1;i<=len;++i) pos[len-i+1]=b[i];
    38     for(int i=1;i<=len;++i)
    39         for(int j=i;j<=len;++j)
    40             for(int k=i;k<=j;++k){
    41                 score[i][j]*=10;
    42                 score[i][j]+=pos[k];
    43                 g[i][i]=pos[i];
    44             }
    45     dfs(1,m,1);
    46     printf("%lld
    ",Max);
    47     for(int i=1;i<=ans2[0];++i) printf("%lld ",ans2[i]);
    48 }
    49 signed main(){
    50     int T;
    51     scanf("%lld",&T);
    52     while(T--) Solve();
    53     return 0;
    54 }
    整数划分方法一

    方法二: 正解DP,用f[i][j]表示前i位数字分为j段的答案,这其实就是一个区间DP板子的变形,我们很容易想到转移方程f[i][j]=max(f[i][j],f[k][j-1]*a[k+1][i])(a数组存储区间内的数字段),之后就是注意要用递归来处理答案的第二部分,就没有什么了。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 const int maxn=2e2+50,INF=0x3f3f3f3f;
     8 int n,sum[maxn],m,g[maxn][maxn],path[maxn],ans;
     9 unsigned long long f[maxn][maxn],a[maxn][maxn];
    10 char str[maxn];
    11 inline int read(){
    12     int s=0,w=1;
    13     char ch=getchar();
    14     while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    15     while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    16     return s*w;
    17 }
    18 void print(int i,int j){
    19     if(!g[i][j]||j==0)return;
    20     path[++ans]=g[i][j];
    21     print(g[i][j],j-1);
    22 }
    23 int main(){
    24     int T=read();
    25     while(T--){
    26         memset(f,0,sizeof(f));
    27         memset(path,0,sizeof(path));
    28         memset(g,0,sizeof(g));
    29         memset(a,0,sizeof(a));
    30         ans=0;
    31         scanf("%s",str+1);
    32         m=read();
    33         n=strlen(str+1);
    34         if(m==1){cout<<str+1<<endl<<str+1<<endl;continue;}
    35         else if(m>n){cout<<0<<endl<<0<<endl;continue;}
    36         for(int i=1;i<=n;i++)a[i][i]=str[i]-'0';
    37         for(int i=1;i<=n;i++){
    38             for(int j=i+1;j<=n;j++){
    39                 a[i][j]=a[i][j-1]*10+a[j][j];
    40             }
    41         }
    42         f[0][0]=1;
    43         for(int i=1;i<=n;i++){
    44             for(int j=1;j<=min(i,m);j++){
    45                 for(int k=j-1;k<i;k++){
    46                     if(f[i][j]<f[k][j-1]*a[k+1][i]){
    47                         f[i][j]=f[k][j-1]*a[k+1][i];
    48                         g[i][j]=k;
    49                     }
    50                     
    51                 }
    52             }
    53         }
    54         cout<<f[n][m]<<endl;
    55         print(n,m);
    56         sort(path+1,path+m+1);
    57         int now=1;
    58         if(f[n][m]==0){
    59             for(int i=1;i<m;i++){
    60                 cout<<a[i][i]<<" ";
    61             }
    62             if(a[m][n]==0){
    63                 for(int i=1;i<=n-now;i++)cout<<0;
    64             }
    65             cout<<endl;
    66         }
    67         else {
    68             for(int i=1;i<m;i++){
    69                 cout<<a[path[i]+1][path[i+1]]<<" ";
    70                 now=path[i+1];
    71             }
    72             if(a[now+1][n]==0){
    73                 for(int i=1;i<=n-now-1;i++)cout<<0;
    74             }
    75             cout<<a[now+1][n]<<endl;
    76         }
    77     }
    78 }
    整数划分方法二

     

     

     

     推荐博客:https://www.cnblogs.com/fengwu2005/p/11289562.html

    这道题和其他区间DP的区别主要在那个存在负值上,我们在一般区间dp的二维上加开第三维,来表示当前的值是最大还是最小,如果我们计算的是加法,直接大大得大小小得小就行了,但是在计算乘法时由于存在负负得正,在计算两个区间的合并时需要将两个区间的大,小值的四种情况分别计算,再取其最大,最小,就可以了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 using namespace std;
     7 int n,shu[150],f[150][150][3],sum[150][150],ff[150]={0},ma,mb,a,b,c,d;
     8 char fu[150];
     9 int main()
    10 {
    11     scanf("%d",&n);
    12     for(int i=1;i<=n;i++){
    13         scanf(" %c%d",&fu[i],&shu[i]);
    14     }
    15     for(int i=n+1;i<=2*n;i++){
    16         shu[i]=shu[i-n];
    17         fu[i]=fu[i-n];
    18     }
    19     for(int i=1;i<=n;i++){
    20         for(int o=1;o<=2*n;o++){
    21             for(int p=1;p<=2*n;p++){
    22                 f[o][p][1]=-9999999;
    23                 f[o][p][2]=9999999;
    24             }
    25         }
    26         for(int q=1;q<=2*n;q++){
    27             f[q][q][1]=shu[q];
    28             f[q][q][2]=shu[q];
    29         }
    30         for(int l=2;l<=n;l++){
    31             for(int j=i;j<=i+n-l;j++)
    32             {
    33                 int k=j+l-1;
    34                 for(int x=j;x<k;x++){
    35                     if(fu[x+1]=='t'&&f[j][k][1]<f[j][x][1]+f[x+1][k][1])f[j][k][1]=f[j][x][1]+f[x+1][k][1];
    36                     if(fu[x+1]=='t'&&f[j][k][2]>f[j][x][2]+f[x+1][k][2])f[j][k][2]=f[j][x][2]+f[x+1][k][2];
    37                     if(fu[x+1]=='x'){
    38                         a=f[j][x][1]*f[x+1][k][1];
    39                         b=f[j][x][2]*f[x+1][k][1];
    40                         c=f[j][x][1]*f[x+1][k][2];
    41                         d=f[j][x][2]*f[x+1][k][2];
    42                         f[j][k][1]=max(max(max(a,b),max(c,d)),f[j][k][1]);
    43                         f[j][k][2]=min(min(min(a,b),min(c,d)),f[j][k][2]);
    44                     }
    45                 }
    46             }
    47         }
    48         ff[i]=f[i][i+n-1][1];
    49     }
    50     ma=-1000000;
    51     for(int i=1;i<=n;i++){
    52         if(ma<ff[i]){
    53             ma=ff[i];
    54             mb=i;
    55         }
    56     }
    57     printf("%d
    ",ma);
    58     for(int i=1;i<=n;i++){
    59         if(ff[i]==ma){
    60             printf("%d ",i);
    61         }
    62     }
    63 }
    多边形

    洛谷P1220关路灯:

     

     这道题我们考虑,如果老张已经刚刚恰好关完了一个区间内的灯,那么他此时一定在区间的左边或右边,因为如果区间两边他都来过,那么他一定路过过区间中间的所有点,关灯又不耗时间,还省电直接断掉就行了,那么此时如果老张想要扩展这个区间,那么他有两种选择:向左或向右,而他此时的位置也有两种情况,即区间左端和区间右端,那么我们就有了四种情况进行转移,有了DP转移的思路这道题就好写了,老张有在左边向左,右边走,在右边向右,左边走四种情况,之后我们提前处理好每个区间之内的每段时间的花费,以方便我们转移,就可以了。

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 const int N=50+5;
     5 using namespace std;
     6 int sum[N][N],cost[N],dp[N][N][2];
     7 struct Node{
     8     int loc,pow;
     9 }a[N];
    10 int main(){
    11     int n,m;
    12     scanf("%d%d",&n,&m);
    13     for(int i=1;i<=n;++i){
    14         scanf("%d%d",&a[i].loc,&a[i].pow);
    15     }
    16     for(int i=1;i<=n;++i){
    17         sum[i][i]=a[i].pow;
    18         for(int j=i+1;j<=n;++j)
    19             sum[i][j]=sum[i][j-1]+a[j].pow;
    20     }
    21     memset(dp,0x3f,sizeof(dp));
    22     if(m>=2){
    23         dp[m-1][m][0]=(a[m].loc-a[m-1].loc)*(sum[1][m-1]+sum[m+1][n]);
    24     }
    25     if(m<=n-1){
    26         dp[m][m+1][1]=(a[m+1].loc-a[m].loc)*(sum[1][m-1]+sum[m+1][n]);
    27     }
    28     for(int len=3;len<=n;++len)
    29         for(int l=1;l<=n-len+1;++l){
    30             int r=l+len-1;
    31             dp[l][r][0]=min(dp[l+1][r][0]+(a[l+1].loc-a[l].loc)*(sum[1][l]+sum[r+1][n]),dp[l+1][r][1]+(a[r].loc-a[l].loc)*(sum[1][l]+sum[r+1][n]));
    32             dp[l][r][1]=min(dp[l][r-1][0]+(a[r].loc-a[l].loc)*(sum[1][l-1]+sum[r][n]),dp[l][r-1][1]+(a[r].loc-a[r-1].loc)*(sum[1][l-1]+sum[r][n]));
    33         }
    34     printf("%d
    ",min(dp[1][n][0],dp[1][n][1]));
    35     return 0;
    36 }
    关路灯

    PATR TWO :总结:

      其实这两天的题目还是比较温柔的,都比较简单,我一个暴力的思路水过了好多题,但是晚上教练指出我在做题的时候还存在诸多不足,我的效率还是很低的,另外,我的急性子的毛病还一直在,在做一道题的时候总是提交多次,思路不够缜密,总是在改完一个点后就匆匆地提交,AC了就完了,WA了就再找再提交,再集训还剩下的两周多的时间之内,我会尽力进行改正的,争取让自己在这段时间内完成属于自己的蜕变。 

  • 相关阅读:
    redis在实践中的一些常见问题以及优化思路(包含linux内核参数优化)
    分库分表基本概念
    产品经理黑话
    在linux虚拟机中安装部署MySQL数据库
    计算机专业本科自考
    在项目中重新搭建一套读写分离+高可用+多master的redis cluster集群
    扔掉工具类,Mybatis一个简单配置搞定数据加密解密!
    两台Linux系统之间传输文件使用scp的解决方案
    博客园积分排名前3000名
    内网穿透在家远程连接公司电脑<强烈收藏,早晚用得上>
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/13184796.html
Copyright © 2011-2022 走看看