在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
编一程序,由文件读入堆数N及每堆的石子数(≤20),
① 选择一种合并石子的方案,使得做N-1次合并,得分的总和最小;
② 选择一种合并石子的方案,使得做N-1次合并,得分的总和最大。
例如,所示的4堆石子,每堆石子数(从最上面的一堆数起,顺时针数)依次为4 5 9 4。则3次合并得分总和最小的方案:8+13+22=43得分最大的方案为:22+18+22=54
解题思路:
根据题意来拟定状态,dp[i][j]从i到j堆石子合并所得总分最大或者最小。
利用分治的思想我们就可以看得出来,我们首先将每相邻的两堆石子合并得到一个得分,然后再扩展到3堆石子合并,3堆由两堆石子的合并情况推出,这样便形成了一种递推的效果,n堆自然能够推断到,只是注意,本题是围绕操场所以最后的石子与起始石子相邻。
View Code
1 #include<iostream> 2 const int N = 102; 3 using namespace std; 4 int sum[N*2][N*2],tt[N*2][N*2],n; 5 int main() 6 { 7 while(scanf("%d",&n)!=EOF) 8 { 9 memset(sum,0,sizeof(sum)); 10 for(int i=1;i<=n;i++) 11 scanf("%d",&sum[i][i]); 12 for(int i=n+1;i<2*n;i++)sum[i][i]=sum[i-n][i-n]; 13 for(int i=1;i<2*n;i++) 14 { 15 for(int j=i+1;j<2*n;j++) 16 { 17 sum[i][j]+=sum[i][j-1]+sum[j][j]; 18 } 19 } 20 for(int i=1;i<2*n;i++) 21 for(int j=1;j<2*n;j++) 22 if(i==j) 23 tt[i][j]=0; 24 else 25 tt[i][j]=100000000; 26 for(int i=2;i<=n;i++) 27 for(int j=1;j<2*n-i+1;j++) 28 { 29 for(int k=j;k<j+i-1;k++) 30 { 31 tt[j][j+i-1]=min(tt[j][k]+tt[k+1][j+i-1]+sum[j][j+i-1],tt[j][j+i-1]); 32 } 33 } 34 int mi=0x7fffffff; 35 for(int i=1;i<=n;i++) 36 if(tt[i][i+n-1]<mi)mi=tt[i][i+n-1]; 37 printf("%d\n",mi); 38 memset(tt,0,sizeof(tt)); 39 for(int i=2;i<=n;i++) 40 for(int j=1;j<2*n-i+1;j++) 41 { 42 for(int k=j;k<j+i-1;k++) 43 { 44 tt[j][j+i-1]=max(tt[j][k]+tt[k+1][j+i-1]+sum[j][j+i-1],tt[j][j+i-1]); 45 } 46 } 47 mi=0; 48 for(int i=1;i<=n;i++) 49 if(tt[i][i+n-1]>mi)mi=tt[i][i+n-1]; 50 printf("%d\n",mi); 51 } 52 return 0; 53 }