题目描述 Description
有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
输入描述 Input Description
第一行一个整数n(n<=100)
第二行n个整数w1,w2…wn (wi <= 100)
输出描述 Output Description
一个整数表示最小合并代价
样例输入 Sample Input
4
4 1 1 4
样例输出 Sample Output
18
发现每合并两堆已合并石子的代价,为每已合并堆中所有石子重量之和;
如先合并1—2堆、3—4堆石子
再将两个新堆合并时,代价为w[1]+w[2]+w[3]+w[4]
而1—2堆石子合并代价为w[1]+w[2]
3—4堆石子合并代价为w[3]+w[4],
故总代价为w[1]+w[2]+w[3]+w[4]+w[1]+w[2]+w[3]+w[4] = 20
显然不是最优解
发现在合并第i—j堆石子时,有合并i—k、k+1—j两堆石子共k(k = j - i)种选择
由此可知我们在求dp[i][j]//dp[i][j]为合并第i到第j堆石子的最小代价
时,必须已知其中任两堆石子i—k、k+1—j的最优合并代价
股采用顺推j,逆推i
枚举k为断点
预处理cost[i][j]为一次合并i—j堆石子(无论先前合并顺序)的代价
递推式:dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + cost[i][j]);
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 2000 + 5;
int w[MAXN],dp[MAXN][MAXN],cost[MAXN][MAXN];
int n;
int main()
{
cin >> n;
for(int i = 1;i <= n;i ++)
{
cin >> w[i];
}
memset(dp,0x3f,sizeof(dp));
for(int i = 1;i <= n;i ++)
for(int j = i;j <= n;j ++)
{
if(i == j)dp[i][j] = 0;
cost[i][j] = cost[i][j - 1] + w[j];
}
for(int j = 2;j <= n;j ++)
for(int i = j - 1;i >= 1;i --)
for(int k = i;k < j;k ++)
{
dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + cost[i][j]);
}
cout << dp[1][n];
return 0;
}
结:感觉dp型题目的问题有两个
一个是递推式,一个是递推过程
怎样保证在已知需要前提的条件下推出结果
也是这题比较难的部分吧