题意:有n个石堆排成环,每次能合并相邻的两堆石头变成新石堆,代价为新石堆石子数,问最少的总代价是多少
思路:先看没排成环之前怎么做:用dp[i][j]表示合并i到j所需的最小代价,那么dp[i][j]就是合并i~k、k+1~j的最小代价,即dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + w[i][j]])。
for(int len = 1; len <= n; len++){ //区间长度 for(int i = 1; i + len - 1 <= n; i++){ //起始位置 int j = i + len - 1; //末尾位置 for(int k = i; k <= j; k++){ if(dp[i][j] > dp[i][k] + dp[k + 1][j] + w[i][j]){ dp[i][j] = dp[i][k] + dp[k + 1][j] + w[i][j]]; } } } }
合并石堆环是,只要把n首尾相接扩展到2n,就可以了。
这个dp还可以用四边形不等式优化
代码:
#include<cstdio> #include<cstring> #include<algorithm> typedef long long ll; using namespace std; const int maxn = 1000 + 10; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; int a[maxn], dp[maxn << 1][maxn << 1], sum[maxn]; int s[maxn << 1][maxn << 1]; //i到j的最佳分割点 int main(){ int n; while(~scanf("%d", &n)){ sum[0] = 0; memset(dp, INF, sizeof(dp)); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); a[i + n] = a[i]; } for(int i = 1; i <= 2 * n; i++){ s[i][i] = i; dp[i][i] = 0; sum[i] = sum[i - 1] + a[i]; } for(int len = 1; len <= n; len++){ //长度 for(int i = 1; i + len - 1 <= 2 * n; i++){ //起始位置 int j = i + len - 1; for(int k = s[i][j - 1]; k <= s[i + 1][j]; k++){ if(dp[i][j] > dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]){ dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]; s[i][j] = k; } } } } int ans = INF; for(int i = 1; i <= n; i++){ ans = min(ans, dp[i][i + n - 1]); } printf("%d\n", ans); } return 0; }