洛谷 P1880 石子合并 破环成链
- 时空限制1s / 128MB
题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输出样例#1:
破环成链
标准做法
43 54
---------------------------------------------------------------------------------
这道题的难点就是如何在一个环上实现DP
环不好搞,那就把它变成一条链吧!
其实就是就是破环成链
做法也挺简单,就是把原来的数列扩展为原来的两倍
然后把长度为n的数列从这个2n的数列的第一个数开始整体右移
一共移n次,即在这个新数列上进行n次循环,找每次移动操作得出的最大或最小值
需要注意每次移动后要清空dp状态值,因为移动后就相当于重新操作,状态值是原先转移前序列的状态值
dp[i][j]表示合并第i个数到第j个数可获得的最大值,dpmi[i][j]则为最小值
sum[i][j]表示第i个数到第j个数的和
注意dpmi[i][i]==0,因为只有一个数时还没合并,所以该状态没有分数
AC代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #define maxn 233 5 using namespace std; 6 int n,dp[maxn][maxn],a[maxn],ansma,ansmi,sum[maxn][maxn],dpmi[maxn][maxn]; 7 int main(){ 8 scanf("%d",&n); 9 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 10 11 for(int i=n+1;i<=n*2;i++) a[i]=a[i-n]; 12 for(int i=1;i<=maxn;i++) 13 for(int j=1;j<=maxn;j++) 14 dpmi[i][j]=1e9; 15 ansmi=1e9; 16 for(int i=1;i<=n*2;i++) 17 for (int j=i;j<=n*2;j++) 18 sum[i][j]+=sum[i][j-1]+a[j]; 19 20 for(int i=1;i<=n;i++){ 21 22 for(int j=i;j<n+i-1;j++){ 23 dp[j][j+1]=sum[j][j+1]; 24 dpmi[j][j+1]=sum[j][j+1]; 25 dpmi[j][j]=0; 26 } 27 dpmi[n+i-1][n+i-1]=0; 28 29 for(int j=2;j<=n-1;j++) 30 for(int g=i;g+j<=n+i-1;g++) 31 for(int k=g;k<=g+j-1;k++){ 32 dp[g][g+j]=max(dp[g][g+j],dp[g][k]+dp[k+1][g+j]+sum[g][g+j]); 33 dpmi[g][g+j]=min(dpmi[g][g+j],dpmi[g][k]+dpmi[k+1][g+j]+sum[g][g+j]); 34 } 35 36 ansma=max(ansma,dp[i][n+i-1]); 37 ansmi=min(ansmi,dpmi[i][n+i-1]); 38 39 for(int i=1;i<=maxn;i++) 40 for(int j=1;j<=maxn;j++) 41 dpmi[i][j]=1e9; 42 memset(dp,0,sizeof(dp)); 43 44 } 45 printf("%d\n%d",ansmi,ansma); 46 return 0; 47 }
-----------------------------------------------------------------------------------------------------------------------------
话说上一次以以上自己想出来的做法AC后,发现以上做法跑得比其他人的慢得多,所以又回来学习了一波
代码简化了许多,更重要的是跑得更快了
破环成链是没错
只是在原来dp过程中可进行很多优化
直接放代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #define maxn 233 5 using namespace std; 6 int read(); 7 int n,dpma[maxn][maxn],sum[maxn],a[maxn],mi,ma,dpmi[maxn][maxn]; 8 int main(){ 9 n=read(); 10 for(int i=1;i<=n;i++){ 11 a[i]=read(); 12 a[i+n]=a[i]; 13 } 14 for(int i=1;i<=2*n;i++) sum[i]=sum[i-1]+a[i]; 15 for(int i=1;i<n;i++)//枚举区间长度 16 for(int j=1;j<=n*2-i;j++){//枚举起点 17 dpmi[j][j+i]=1e9; 18 for(int k=j;k<j+i;k++){//枚举中点 19 dpma[j][j+i]=max(dpma[j][j+i],dpma[j][k]+dpma[k+1][j+i]+sum[j+i]-sum[j-1]); 20 dpmi[j][j+i]=min(dpmi[j][j+i],dpmi[j][k]+dpmi[k+1][j+i]+sum[j+i]-sum[j-1]); 21 } 22 } 23 mi=1e9;ma=0; 24 for(int i=1;i<n;i++){ 25 ma=max(ma,dpma[i][i+n-1]); 26 mi=min(mi,dpmi[i][i+n-1]); 27 } 28 printf("%d\n%d",mi,ma); 29 return 0; 30 } 31 int read(){ 32 int ans=0,f=1;char c=getchar(); 33 while('0'>c||c>'9'){if(c=='-')f=-1;c=getchar();} 34 while('0'<=c&&c<='9')ans=ans*10+c-48,c=getchar();return ans*f; 35 }
然后就是从512ms到12ms的差距T_T