您的位置:首页 > 其它

NYOJ 737 石子归并问题 (区间DP1)

2017-01-10 14:36 441 查看
题目链接:见这里

题意:中文题目

解题方法:基础区间DP。要求n个石子归并,我们根据dp的思想划分成子问题,先求出每两个合并的最小代价,然后每三个的最小代价,依次知道n个。定义状态dp [ i ] [ j ]为从第i个石子到第j个石子的合并最小代价。则有:

dp[i][j]=min(dp[i][k]+dp[k+1][j])

那么我们就可以从小到大依次枚举让石子合并,直到所有的石子都合并。

复杂度: O(n * n * n)

int n, dp[maxn][maxn],a[maxn], sum[maxn];

int main()
{
while(scanf("%d", &n) != EOF)
{
CLR(sum, 0);
CLR(dp, 0);
REP2(i, 1, n) scanf("%d", &a[i]);
REP2(i, 1, n) sum[i] = sum[i - 1] + a[i];
for(int len = 2; len <= n; len++){
for(int i = 1; i + len - 1 <= n; i++){
int j = i + len - 1;
dp[i][j] = INF;
for(int k = i; k < j; k++){
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i -1]);
}
}
}
printf("%d\n", dp[1]
);
}
return 0;
}


这里哈有一种优化这个DP的方式,叫做平行四边形优化,记用一个s【i】【j】=k 表示区间 i—j 从k点分开才是最优的,这样的话我们就可以优化掉一层复杂度,变为O(n * n)

代码如下:

int n, s[maxn][maxn], dp[maxn][maxn],a[maxn], sum[maxn];

int main()
{
while(scanf("%d", &n) != EOF)
{
CLR(sum, 0);
CLR(dp, 0);
REP2(i, 1, n) scanf("%d", &a[i]), s[i][i] = i;
REP2(i, 1, n) sum[i] = sum[i - 1] + a[i];
for(int len = 2; len <= n; len++){
for(int i = 1; i + len - 1 <= n; i++){
int j = i + len - 1;
dp[i][j] = INF;
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;
}
}
}
}
printf("%d\n", dp[1]
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: