天天看點

HihoCoder - 1636 Pangu and Stones——區間dp

題意:在石子合并的基礎上做了限制,規定每次至少合并連續l堆石子,至多合并r堆石子

思路:定義dp【i】【j】【k】為區間【i,j】包含k堆石子時的最小值, 最終結果就是dp【1】【n】【1】,即整個區間最終合并為1堆的最小值,明顯dp【i】【j】【1】是求解的重點。

求解dp【i】【j】【1】的狀态轉移方程為:dp【i】【j】【1】 = min(dp【i】【j】【1】, dp【i】【j】【k】 + sum【j】 - sum【i-1】)(2 《= k 《= r(r是題目給定的))

其中sum【】預處理字首和,用于計算任意一段連續區間的和,很明顯如果想要把區間【i,j】中的所有堆合并為1個堆,無論如何都要花費這個區間的總和,dp【i】【j】【k】(2 《= k 《= r)則是枚舉不同的劃分,最終找到一個最小值

求解dp【i】【j】【k】(2 《= k 《= r)的狀态轉移方程為:dp【i】【j】【k】 = min(dp【i】【j】【k】, dp【i】【p】【1】+dp【p+1】【j】【k-1】)(i《=p《j)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 105;
const int INF = 0x3f3f3f3f;
int n, l, r, a[maxn], sum[maxn], dp[maxn][maxn][maxn];
int main() {
    while (~scanf("%d %d %d", &n, &l, &r)) {
        sum[0] = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            sum[i] = sum[i-1] + a[i];
        }
        memset(dp, INF, sizeof(dp));
        for (int i = 1; i <= n; i++) dp[i][i][1] = 0;
        for (int len = 1; len <= n; len++) {
            for (int i = 1; i + len - 1 <= n; i++) {
                int j = i + len - 1;
                for (int k = min(r, len); k >= 2; k--) {
                    for (int p = i; p < j && j - p >= k - 1; p++) {
                        dp[i][j][k] = min(dp[i][j][k], dp[i][p][1] + dp[p+1][j][k-1]);
                    }
                    if (k >= l) dp[i][j][1] = min(dp[i][j][1], dp[i][j][k] + sum[j] - sum[i-1]);
                }
            }
        }
        printf("%d\n", dp[1][n][1] == INF ? 0 : dp[1][n][1]);
    }
    return 0;
}