题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1592
题意:给定一堆石子,每次只能合并相邻两堆,每次的合并代价为两者石子数之和,求最后只剩下一堆时的最小合并代价。
刚开始,想到的是贪心,每次合并相邻和值最小的两堆就行了,这题和 POJ 3186 Treats for the Cows以及紫书上的“最优矩阵链乘”差不多,初看看似贪心,实则是Dp问题,但是都不会,呵呵。。。。只能看别人的代码,然后再写。。。
思路:用dp[i][j]表示合并区间[i,j]所需要的最小代价,那么,状态转移方程为:dp[i][j]=max(dp[i]k]+dp[k+1][j]+sum[i][j]),i<=k<=j。显然,对于我来讲,这个式子是不好直接实现递推的,但是发现这个递推式子和紫书上的最优矩阵链乘差不多,都表示:要求大区间的解,得先知道小区间的解,所以,可以把上述状态进行转换一下:用dp[i][j]表示,以i为起点,长度为j的解,则状态转移方程变为:dp[i][j]=min(dp[i][j],dp[i][k]+dp[i+k+1][j-k-1]+sum[i][i+j]);这样一来,就好实现递推方程了。
代码:
#include<iostream>
#include<cstdio>
#define maxn 110
#define INF 0x7fffffff
using namespace std;
int N,A[maxn],dp[maxn][maxn],sum[maxn];
int main(){
//freopen("in.txt","r",stdin);
int T;cin>>T;
while(T--){
cin>>N;
for(int i=1;i<=N;i++)
scanf("%d",A+i);
sum[0]=0;
for(int i=1;i<=N;i++)
sum[i]=sum[i-1]+A[i];
for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dp[i][j]=INF;//初始化
for(int i=1;i<=N;i++) dp[i][0]=0;//初始化
for(int j=1;j<N;j++) for(int i=1;i+j<=N;i++)//先枚举长度j
for(int k=0;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[i+k+1][j-k-1]+sum[i+j]-sum[i-1]);
cout<<dp[1][N-1]<<endl;
}
return 0;
}