Pangu and Stones HihoCoder - 1636[區間dp]
阿新 • • 發佈:2018-11-07
題意:有n個數字,每一次只能合併最少l個最多r個連續的數字,合併的代價為他們的和,詢問最小代價把他們合併成一堆,如果不能合併成一堆輸出0。
總結:比賽的時候只是想到了一點點區間dp,但是dp的含義沒有完全想明白,導致轉移方程寫不出來,然後卡到了最後,後來看了一下題解dp的含義頓悟了一點點,區間dp接觸的還是太少了。
題解:區間dp。
我們規定
為合併
區間為
所需要的最小花費。然後得到下列轉移方程。
- 我們在這裡預處理合併成一堆的情況,x屬於
k為1的時候需要預處理,因為後面的所有狀態都是由合併成一堆的情況轉移過去的,在合併為1堆的時候需要滿足堆數在 區間內,後面的轉移無需再次考慮。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define per(i, a, b) for(int i = a; i >= b; i--)
#define fi first
#define se second
const int maxn = 1e3 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod = 100003;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '0') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return f * x;
}
int n, l, r, a[maxn];
ll sum[102][102];
ll dp[102][102][102];
int main() {
while(~scanf("%d%d%d", &n, &l, &r)) {
rep(i, 0, 101) rep(j, 0, 101) rep(k, 0, 101) dp[i][j][k] = inf;
met(sum, 0);
rep(i, 1, n) a[i] = read();
rep(i, 1, n) {
rep(j, i, n) {
sum[i][j] = sum[i][j - 1] + a[j];
dp[i][j][j - i + 1] = 0;
}
}
for(int d = 1; d <= n; d++) {
for(int i = 1; i + d <= n; i++) {
for(int j = i; j < i + d; j++) {
for(int k = l - 1; k <= r - 1; k++) {
dp[i][i + d][1] = min(dp[i][i + d][1], dp[i][j][k] + dp[j + 1][i + d][1] + sum[i][i + d]);
}
}
for(int k = 1; k <= n; k++) {
for(int j = i; j < i + d; j++) {
dp[i][i + d][k] = min(dp[i][i + d][k], dp[i][j][k - 1] + dp[j + 1][i + d][1]);
}
}
}
}
if(dp[1][n][1] == (ll)inf) puts("0");
else printf("%lld\n", dp[1][n][1]);
}
return 0;
}