区间DP——石子合并一家人(C++)
A. 石子合并<一>
入门版:一线
题目描述
有N堆石子排成一排(n<=100),现要将石子有次序地合并成一堆,规定每次只能选相邻的两堆合并成一堆,并将新的一堆的石子数,记为改次合并的得分,编一程序,由文件读入堆数n及每堆石子数(<=200);
(1)选择一种合并石子的方案,使得做n-1次合并,得分的总和最少
(2)选择一种合并石子的方案,使得做n-1次合并,得分的总和最多
输入格式
第一行为石子堆数n 第二行为每堆石子数,每两个数之间用一空格分隔。
输出格式
从第1行为得分最小第二行是得分最大。
样例
样例输入
4
4 5 9 4
样例输出
44
54
首先,本文讲的是DP石子合并,而非贪心合并果子二者的区别就在于此——每次只能选相邻的两堆合并成一堆还是任意合并。
思路
设dpmin[i]--[j]表示
附上代码
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 int n,dpmax[500][500],dpmin[500][500]/*前i-j最优解*/,sum[500][500],read[500],tot; 6 int main() 7 { 8 cin>>n; 9 for(int i=1;i<=n;i++) 10 { 11 cin>>read[i]; 12 } 13 for(int i=1;i<=n;i++) 14 { 15 tot=read[i]; 16 for(int j=i+1;j<=n;j++) 17 { 18 tot+=read[j]; 19 sum[i][j]=tot; 20 } 21 } 22 memset(dpmin,0x3f,sizeof(dpmin)); 23 for(int i=1;i<=n;i++) 24 dpmin[i][i]=0; 25 for(int len=1;len<n;len++) 26 { 27 for(int i=1;i<=n&&len+1<=n;i++) 28 { 29 int j=len+i; 30 for(int k=i;k<=j;k++) 31 { 32 dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]); 33 dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]); 34 } 35 dpmax[i][j]+=sum[i][j]; 36 dpmin[i][j]+=sum[i][j]; 37 } 38 } 39 cout<<dpmin[1][n]<<endl; 40 cout<<dpmax[1][n]; 41 }
B. 石子合并<2>
进阶版:拆分
题目描述
在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入格式
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式
输出共2行,第1行为最小得分,第2行为最大得分
样例
样例输入
4
4 4 5 9
样例输出
43
54
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 int n,dpmax[1001][1001],dpmin[1001][1001],sum[1001][1001],read[1001],tot,maxi,mini=99999999; 6 int main() 7 { 8 cin>>n; 9 for(int i=1;i<=n;i++) 10 cin>>read[i]; 11 for(int i=n+1;i<=2*n-1;i++) 12 read[i]=read[i-n]; 13 for(int i=1;i<=2*n-1;i++) 14 { 15 tot=read[i]; 16 for(int j=i+1;j<=2*n-1;j++) 17 { 18 tot+=read[j]; 19 sum[i][j]=tot; 20 } 21 } 22 memset(dpmin,0x3f,sizeof(dpmin)); 23 for(int i=1;i<=2*n-1;i++) 24 dpmin[i][i]=0; 25 for(int len=1;len<n;len++) 26 { 27 for(int i=1;i<=2*n-1&&len+i<=2*n-1;i++) 28 { 29 int j=len+i; 30 for(int k=i;k<=j;k++) 31 { 32 dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]); 33 dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]); 34 } 35 dpmax[i][j]+=sum[i][j]; 36 dpmin[i][j]+=sum[i][j]; 37 } 38 } 39 for(int i=1;i<=n;i++) 40 { 41 maxi=max(maxi,dpmax[i][i+n-1]); 42 mini=min(mini,dpmin[i][i+n-1]); 43 } 44 cout<<mini<<endl<<maxi; 45 }
C. 石子合并<3>
优化版:数学支持
题目描述
在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆最大得分.
输入格式
数据的第1行试正整数N,1≤N≤2000,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式
输出共1行,最大得分
样例
样例输入
4
4 4 5 9
样例输出
54
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 unsigned int n,dpmax[4011][4011],sum[4011][4011],read[4011],tot,maxi,mini=99999999,k; 6 int main() 7 { 8 cin>>n; 9 for(int i=1;i<=n;i++) 10 cin>>read[i]; 11 for(int i=n+1;i<=2*n-1;i++) 12 read[i]=read[i-n]; 13 for(int i=1;i<=2*n-1;i++) 14 { 15 tot=read[i]; 16 for(int j=i+1;j<=2*n-1;j++) 17 { 18 tot+=read[j]; 19 sum[i][j]=tot; 20 } 21 } 22 for(int len=1;len<n;len++) 23 { 24 for(int i=1;i<=2*n-1&&len+i<=2*n-1;i++) 25 { 26 int j=len+i; 27 dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+1][j]); 28 dpmax[i][j]+=sum[i][j]; 29 } 30 } 31 for(int i=1;i<=n;i++) 32 maxi=max(maxi,dpmax[i][i+n-1]); 33 cout<<maxi; 34 }