洛谷P1880 [NOI1995]石子合并

链接:https://www.luogu.org/problemnew/show/P1880
思路:再次接触区间dp,这次感觉比第一次理解更深入了一些,一般的线性dp是从前往后递推,但有些情形是需要从左右两个小区间合并为一个大区间,这时候就是区间合并问题了,石子合并是最经典的区间合并问题,区间dp首先枚举区间长度,从2开始(1没有意义且会影响最终结果),然后枚举左端点,然后得出右端点,最后枚举中间断层的地方(k表示在第k个数后断开)。对于环形,我们可以把环拆成长度2倍的链,只需要枚举一下链的起点就可以得到换上所有答案的最值。
代码:

#include<bits/stdc++.h>
using namespace std;
int n;
typedef long long ll;
const int maxn = 210;
int a[maxn];
ll maxv[maxn][maxn];
ll minv[maxn][maxn];
ll sum[maxn];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[n+i] = a[i];//拆环为链
    }
    for(int i=1;i<=2*n;i++){
        sum[i] = sum[i-1]+a[i];
    }
    for(int i=1;i<=2*n;i++){
        for(int j=1;j<=2*n;j++){
            minv[i][j] = 1e18;
        }
    }
    for(int i=1;i<=2*n;i++)maxv[i][i] = minv[i][i] = 0; 
    for(int len=2;len<=2*n;len++){//从2开始枚举长度(包含左端点本身)
        for(int l=1;l+len-1<=2*n;l++){//枚举左端点
            int r = len+l-1;//得出右端点
            for(int k=l;k<r;k++){//枚举断点(在k的右边断开)
                maxv[l][r] = max(maxv[l][r],maxv[l][k]+maxv[k+1][r]);
                minv[l][r] = min(minv[l][r],minv[l][k]+minv[k+1][r]);         
            }
            //补上合并代价
            maxv[l][r]+=sum[r]-sum[l-1];
            minv[l][r]+=sum[r]-sum[l-1];
        }
    }
    ll res1 = 0,res2 = 1e18;
    for(int i=1;i<=n+1;i++){//找到环上所有方案的最值
        res1 = max(res1,maxv[i][i+n-1]);
        res2 = min(res2,minv[i][i+n-1]);
    }
    printf("%lld\n%lld\n",res2,res1);
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 归去来兮。 1.1 说明 本篇为《挑战程序设计竞赛(第2版)》[http://www.ituring.com.cn...
    尤汐Yogy阅读 14,451评论 0 160
  • 简介 区间dp,顾名思义就是在一段区间上进行动态规划。对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是...
    Steven1997阅读 6,562评论 0 2
  • 算法思想贪心思想双指针排序快速选择堆排序桶排序荷兰国旗问题二分查找搜索BFSDFSBacktracking分治动态...
    第六象限阅读 3,262评论 0 0
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,771评论 0 33
  • “嗨,不记得我啦?” “你是……我们认识吗?” “军训,我们一个连队的,记忆力不行啊,同学。” “嘿嘿,不好意思哈...
    四西阅读 512评论 2 5