先来看直线的:
N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 100) 第2 - N + 1:N堆石子的数量(1 <= Aii <= 10000)
Output
输出最小合并代价
Sample Input
4 1 2 3 4
Sample Output
19
虽然知道这是一道dp题目,但是刚开始的时候怎么也想不出怎么做状态转移,于是又是blog和csdn一顿搜,终于搞出来了。
我们设dp[i][j]是从第i堆石子到第j堆石子合并的最小代价,sum[i]是前i堆石子的数量之和,那么状态转移方程就是:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
就是把第i堆到第j堆石子分成第i堆到第k堆石子、第k+1堆到第j堆石子,再加上这两大堆石子合并(sum[j]-sum[i-1]),在i<=k<j的范围遍历,找到最小值即可。
由于d[i][j]依赖于d[i][k]和d[k+1][j],所以我们要根据len(j-i)的大小从小到大来循环,这样才能每次都能确保d[i][j]分解的d[i][k]和d[k+1][j]在这之前计算过。
具体细节看代码:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 #define inf 0x3f3f3f3f 5 const int maxn=101; 6 int n,a[maxn],dp[maxn][maxn],sum[maxn]; 7 int main() 8 { 9 while(cin>>n) 10 { 11 12 for(int i=1;i<=n;i++) 13 { 14 cin>>a[i]; 15 sum[i]=sum[i-1]+a[i]; 16 } 17 for(int i=1;i<=n;i++) 18 { 19 for(int j=1;j<=n;j++) 20 { 21 dp[i][j]=i==j?0:inf; 22 } 23 } 24 for(int len=1;len<n;len++) 25 { 26 for(int i=1;i+len<=n;i++) 27 { 28 int j=i+len; 29 for(int k=i;k<j;k++) 30 { 31 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); 32 } 33 } 34 } 35 cout<<dp[1][n]<<endl; 36 } 37 }
如果石堆摆成环状呢?
思路就是把它拆成直线型的,比如说题目的1234,我们把它拆成线性的以后就是1234123,然后在从这里找出合并相邻的4堆石子的最小代价就行了,做法跟原来的差不多,不过最后再搜一个最小值,洛谷1880就有这道题,不过洛谷的题还要求最大代价,这是我a了洛谷的代码:
1 #include<iostream> 2 #include<cstring> 3 #define inf 0x3f3f3f3f 4 using namespace std; 5 const int maxn=2e3+5; 6 int n,a[maxn],dp_min[maxn][1001],dp_max[maxn][1001],sum[maxn],s=inf; 7 int main() 8 { 9 cin>>n; 10 for(int i=1;i<=n;i++) 11 { 12 cin>>a[i]; 13 sum[i]=sum[i-1]+a[i]; 14 } 15 for(int i=n+1;i<=n*2-1;i++) 16 { 17 a[i]=a[i-n]; 18 sum[i]=sum[i-1]+a[i]; 19 } 20 for(int len=1;len<n;len++) 21 { 22 for(int i=1;i+len<=2*n;i++) 23 { 24 int j=i+len; 25 dp_min[i][j]=inf; 26 for(int k=i;k<j;k++) 27 { 28 dp_min[i][j]=min(dp_min[i][j],dp_min[i][k]+dp_min[k+1][j]+sum[j]-sum[i-1]); 29 dp_max[i][j]=max(dp_max[i][j],dp_max[i][k]+dp_max[k+1][j]+sum[j]-sum[i-1]); 30 } 31 } 32 } 33 int ans_min=inf,ans_max=0; 34 for(int i=1;i<=n;i++) 35 { 36 ans_min=min(ans_min,dp_min[i][i+n-1]); 37 ans_max=max(ans_max,dp_max[i][i+n-1]); 38 } 39 cout<<ans_min<<endl<<ans_max<<endl; 40 }
为什么跑到洛谷去a题了?因为这个做法在这道题会T啊<(_ _)>,O(n^3)的算法,n又是1000,难顶。
不过还好,既然有这道题,一定就有解法。在网上查了一通,说是要用什么四边形不等式优化成O(n^2),看的我真的脑壳疼。
不学会怎么行?不过我就算懂了一些,也表达不出来,给你们推荐这个大佬的blog:https://www.cnblogs.com/cglongge/p/9451161.html
原文地址:https://www.cnblogs.com/hailin545/p/12264451.html