直线石子合并(区间DP)

Posted chr1stopher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直线石子合并(区间DP)相关的知识,希望对你有一定的参考价值。

石子合并


时间限制:1000 ms  |  内存限制:65535 KB


描述
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值和最大值。

输入
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

输出
输出总代价的最小值以及最大值(中间以空格隔开),占单独的一行

样例输入
3
1 2 3
7
13 7 8 16 21 4 18

样例输出
9 11
239 365

 

 

思路:

该题为区间DP好题,这里简单谈一谈区间DP : 

  区间DP,就是在某一区间内满足某个性质,比如最简单的最大最小,一般区间dp有明显的区间性,区别一些线性DP,线性DP每个状态都由前一个转移而来,区间dp也是,但是是由前面区间转移而来,区间dp一般问的是某个区间的某个性质,区间dp从区间是1,是2,是3一步一步转化过来,区间为2就是两个区间为1相加,这样所有区间为2的都就转移出来,如果区间为4的,可能是区间1和区间3,也可能是区间2和区间2,因为区间1区间2区间3所有情况都枚举过,所以直接枚举转移就好,简单的区间dp代码有很强的套路性。(看完可能不认识“区”这个字了= =)

  区间动规一般都是三层for循环, 前两层用来控制区间长度, 最后一层用来枚举区间内最后一次的位置, 还有需要注意的是区间要从小到大, 因为动态规划就是后面得用到前面得出的结果递推后面的结果。

状态转移方程

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

/

dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);

 

 

AC code:

 

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<string>
#include<queue>
#include<utility>
using namespace std;
typedef long long ll;
const int MX = 1e2+7;
const int INF = 0x3f3f3f3f;
int dp1[MX][MX],dp2[MX][MX],sum[MX][MX];
int n,a[MX];

int main(int argc, char const *argv[])
{
    while(~scanf("%d",&n))
    {
        //一定要记得初始化
        memset(dp1,0,sizeof(dp1));
        memset(dp2,0,sizeof(dp2));
        memset(sum,0,sizeof(sum));
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
            sum[i][i] = a[i];
        }
        for(int i = 1;i <= n;i++)
            for(int j = i;j <= n;j++)
                dp1[i][j] = i == j ? 0 : INF;//dp[i][i]只有一个数字无法合并,代价为0
        for(int len = 1;len < n;len++)//枚举区间长度
        {
            for(int i = 1;i + len <= n;i++)//枚举区间起点
            {
                int j = i + len;//枚举区间终点
                for(int k = i;k < j;k++)//枚举区间断点
                {
                    sum[i][j] = sum[i][k]+sum[k+1][j];//sum[i][j]是用来储存i~j石子总数,一般写法是用前缀和计算,这里同样采用动态规划
                    dp1[i][j] = min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[i][j]);
                    dp2[i][j] = max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[i][j]);
                }
            }
        }
        printf("%d %d
",dp1[1][n],dp2[1][n]);
    }
    return 0;    
}

  

 

以上是关于直线石子合并(区间DP)的主要内容,如果未能解决你的问题,请参考以下文章

台州 OJ 2793 石子归并 区间DP

合并石子 区间dp水题

从合并石子学区间DP

石子合并(区间dp)

[NYIST737]石子合并(区间dp)

区间dp