1. 程式人生 > >HihoCoder - 1636 Pangu and Stones(區間DP)

HihoCoder - 1636 Pangu and Stones(區間DP)

algorithm sca 問題 con -s 轉移 one nes iostream

有n堆石子,每次你可以把相鄰的最少L堆,最多R堆合並成一堆。

問把所有石子合並成一堆石子的最少花費是多少。

如果不能合並,輸出0。

石子合並的變種問題。

用dp[l][r][k]表示將 l 到 r 之間的石子合並成 k 堆。

顯然是k == 1 時,合並才是需要花費代價的。k >= 2時轉移的時候不需要加代價。

這個我當時非常不理解。然後後來想想確實是這樣的。因為k >= 2的狀態必然是由 k == 1的時候轉移過來的。

就是說將[l, r]分成k堆,必然要有幾堆合並成一堆。

同理,合並區間長度限制的時候也只在k == 1的時候考慮就好了。

轉移的時候分開按情況轉移就好了。

記憶化搜索的形式老是TLE。我也不知道為啥。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
int n, L, R;
int dp[maxn][maxn][maxn];
int sum[maxn], a[maxn];

//int DP(int l, int r, int k)
//{ // if (k > r-l+1) return INF; // if (k == r-l+1) return dp[l][r][k] = 0; // if (dp[l][r][k] != INF) return dp[l][r][k]; // // if (k == 1) // { // for (int j = L; j <= R; j++) // for (int i = l; i <= r-1; i++) // dp[l][r][k] = min(dp[l][r][k], DP(l, i, j-1)+DP(i+1, r, 1)+sum[r]-sum[l-1]);
// } // else // for (int i = l; i <= r-1; i++) // dp[l][r][k] = min(dp[l][r][k], DP(l, i, k-1)+DP(i+1, r, 1)); // // return dp[l][r][k]; //} int main() { while(~scanf("%d%d%d", &n, &L, &R)) { for (int i = 1; i <= n; i++) scanf("%d", &a[i]), sum[i] = sum[i-1]+a[i]; memset(dp, INF, sizeof(dp)); //printf("%d %d\n", dp[1][1][1], 0x3f3f3f3f); for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) dp[i][j][j-i+1] = 0; for (int len = 2; len <= n; len++) for (int l = 1; l+len-1 <= n; l++) { int r = l+len-1; for (int j = 2; j <= len; j++) for (int k = l; k <= r-1; k++) dp[l][r][j] = min(dp[l][r][j], dp[l][k][j-1]+dp[k+1][r][1]); for (int j = L; j <= R; j++) for (int k = l; k <= r-1; k++) dp[l][r][1] = min(dp[l][r][1], dp[l][k][j-1]+dp[k+1][r][1]+sum[r]-sum[l-1]); } // int ans = DP(1, n, 1); printf("%d\n", dp[1][n][1]==INF ? 0:dp[1][n][1]); } }

HihoCoder - 1636 Pangu and Stones(區間DP)