题目描述
有一排石子,共n 堆。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分。
题解
首先由直接动态规划的方法来做,即
for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) for(int k=i;k<=j;k++) { f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+cost[j]-cost[i-1]); }
但是如果N的值超过了3000的话,这个算法就明显会爆炸的,所以我们应当采取一定的优化,经过计算我们可以发现这个题目是满足四边形不等式的,这里所用的优化呢是四边形不等式优化。
我们定义阶段为区间长度len,状态为f[i][j]合并i到j的最小费用。
注意:因为我们的阶段决定了,在计算s[i][j]时,s[i][j-1]和s[i+1][j]都已经计算出来了
代码
//FZOJ1555 合并石子(求最小值)四边形不等式优化 #include<bits/stdc++.h> using namespace std; int f[101][101],s[101][101],a[101],sum[101],q; int main() { int i,j,k,n,len,q; scanf("%d",&n); memset(sum,0,sizeof(sum)); memset(f,60,sizeof(f)); memset(s,0,sizeof(s)); for(i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(i=1;i<=n;i++) { f[i][i]=0; s[i][i]=i; } for(len=1;len<n;len++) for(i=1;i<=n-len;i++) { j=i+len; q=sum[j]-sum[i-1]; for(k=s[i][j-1];k<=s[i+1][j];k++) { if(f[i][j]>f[i][k]+f[k+1][j]+q) { f[i][j]=f[i][k]+f[k+1][j]+q; s[i][j]=k; } } } printf("%d\n",f[1][n]); return 0; }