HDU 3506 Monkey Party(区间DP)题解

题意:有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;
}

 

posted @ 2019-01-22 21:05  KirinSB  阅读(414)  评论(0编辑  收藏  举报