您的位置:首页 > 其它

nyoj 737 石子合并(一)。区间dp

2016-12-27 00:13 495 查看
http://acm.nyist.net/JudgeOnline/problem.php?pid=737

数据很小,适合区间dp的入门

对于第[i, j]堆,无论你怎么合并,无论你先选哪两堆结合,当你把[i, j]合成一堆的那一步的时候,花费肯定就是sum[i....j]

可以用纸模拟下。

那么我们设dp[i][j]表示把i...j堆合成一堆的时候的最小花费。

比如dp[1][1] = 0。dp[1][2] = a[1] + a[2];

那么要求dp[i][j],则可以是dp[i][k] + dp[k + 1][j] + cost

注意dp的时候的顺序,因为要求dp[1]
,则需要用到dp[1][k]和dp[k]

你需要考虑下怎么for,才能使得子问题已经被算出,建议一开始用dfs + 记忆化做。

这里dp的顺序应该是先算出2个集合的,3个、4个、......

就是先算出dp[1][2], dp[2][3],这使得求dp[1][3]成为可能。

all dp[i][i] = 0

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
int n;
const int maxn = 1e3 + 20;
int dp[maxn][maxn];
int s[maxn][maxn];
int sum[maxn];
void work() {
for (int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
sum[i] = sum[i - 1] + x;
dp[i][i] = 0;
s[i][i] = i;
}
for (int dis = 1; dis <= n - 1; ++dis) {
for (int be = 1; be + dis <= n; ++be) {
int en = be + dis;
dp[be][en] = inf;
int t = s[be][en];
for (int k = s[be][en - 1]; k <= s[be + 1][en]; ++k) {
if (k + 1 > en) break;
if (dp[be][en] >= dp[be][k] + dp[k + 1][en] + sum[en] - sum[be - 1]) {
dp[be][en] = dp[be][k] + dp[k + 1][en] + sum[en] - sum[be - 1];
t = k;
}
}
s[be][en] = t;
}
}
cout << dp[1]
<< endl;
}
int main() {
#ifdef local
freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
while (scanf("%d", &n) != EOF) work();
return 0;
}


View Code
简单来说,就是设s[i][j]表示第i---j堆石子合并的时候,在第s[i][j]那里合并,是最优的。

那么可以证明的是:s[i][j - 1] <= s[i][j] <= s[i + 1][j]

那么只需要枚举里面的值就好了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: