今天已经是集训的第二天了,今天的考试主要考了区间DP,其实这一段我并不很熟。
其实这是我第二遍写这篇博客了,原来那篇被某邹姓少年弄没了,我只好再写一遍了。
PART ONE:DP复习二:
其实这道题在上午的时候我写出来了。只是我用的是暴搜,可能数据比较水就让我过了,但是其实这道题的正解我是不会写的,于是就在这里补一下吧,其实我感觉暴搜在DP这里的应用领域还是挺广的,包括昨天和今天好多题我都是用暴搜做的,还过了不少。但是从今天下午开始暴搜的应用就不怎么广了,好几道题都是只能得到一半左右的分数
方法一: 暴搜,用dfs的三个变量分别表示当前所处理到的位数,当前所分成的段数量及此时的乘积,暴搜的思路挺好想的,就是看数据,特别容易超时,所以最好慎用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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数组存储区间内的数字段),之后就是注意要用递归来处理答案的第二部分,就没有什么了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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的二维上加开第三维,来表示当前的值是最大还是最小,如果我们计算的是加法,直接大大得大小小得小就行了,但是在计算乘法时由于存在负负得正,在计算两个区间的合并时需要将两个区间的大,小值的四种情况分别计算,再取其最大,最小,就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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转移的思路这道题就好写了,老张有在左边向左,右边走,在右边向右,左边走四种情况,之后我们提前处理好每个区间之内的每段时间的花费,以方便我们转移,就可以了。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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了就再找再提交,再集训还剩下的两周多的时间之内,我会尽力进行改正的,争取让自己在这段时间内完成属于自己的蜕变。