设有 N堆石子排成一排,其编号为 1,2,3,…,N。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
例如有 4 堆石子分别为 1 3 5 2
, 我们可以先合并 1、2堆,代价为 4,得到 4 5 2
, 又合并 1、2堆,代价为 9,得到 9 2
,再合并得到 11,总代价为 4+9+11=24;
如果第二步是先合并 2、3 堆,则代价为 7,得到 4 7
,最后一次合并代价为 11,总代价为 4+7+11=22。
问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
输入格式
第一行一个数 N 表示石子的堆数 N。
第二行 N 个数,表示每堆石子的质量(均不超过 1000)。
输出格式
输出一个整数,表示最小代价。
数据范围
1≤N≤300
输入样例:
4
1 3 5 2
输出样例:
22
思路:这里明确有个规则只能合并相邻的两堆石子,所以为dp问题,如果不要求只能合并相邻两堆石子,那么就是哈夫曼树的贪心问题。
最后的那一堆也是由相邻的两堆合并而来,由此得到问题其实就是考虑两堆石子合并的所有可能,f(i,j)=min(f(i,j),f(i,k)+f(k+1,j)+s[j]-s[i-1]) 这个循坏就可以算出素有f(i,j)的合并中最小的那一个。
完整代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 310;
int n, dp[N][N],s[N];int main() {/*memset(dp,0x3f,sizeof dp);memset(s,0,sizeof s);*/cin >> n;for (int i = 1; i <= n; i++){cin >> s[i];s[i]+=s[i-1];}for (int len = 2; len<= n; len++){for (int i = 1; i+len-1 <= n; ++i){int j=len+i-1;dp[i][j]=1e8;for(int k=i;k<j;k++){dp[i][j]= min(dp[i][k]+dp[k+1][j]+s[j]-s[i-1],dp[i][j]);}}}cout<<dp[1][n]<<endl;
}