zoukankan      html  css  js  c++  java
  • 直线石子合并(区间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;    
    }
    

      

  • 相关阅读:
    Apple Mac OS X每日一技巧002:如何修改打开文档的默认程序
    Baby听世界03:这就是电影啊??!!
    八卦一下,51JOB人才库搜索上海地区含有.NET关键字的期望收入和对应的人数
    接口和抽象类的区别
    总结virtual override sealed new在方法上的应用
    JQuery学习笔记01JQuery初接触
    《C#妹妹和ObjectiveC阿姨对话录》(03)NSString--再遇狗狗
    Mac新手常见问题
    Baby听世界01:产检
    Apple Mac OS X每日一技巧001:如何添加删除开机启动程序项
  • 原文地址:https://www.cnblogs.com/chr1stopher/p/10322371.html
Copyright © 2011-2022 走看看