洛谷 P1880 化环为链+区间dp

https://www.luogu.org/problemnew/show/P1880

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1: 复制

4
4 5 9 4

输出样例#1: 复制

43
54

https://blog.csdn.net/xiji333/article/details/86677547

思路:经典dp问题,上面那个链接是这道题的弱化版,即不是环形的,稍微简单一点,思路可以看那一篇博客。这里讲一下环怎么处理,我们考虑化环为链,比如环状的1 3 5可以写成链状的 1 3 5 1 3,即将长度为n的环拆成长度为2*n-1的链,拆完之后就跟上面那道题的处理一模一样了。既求最大值又求最小值开两个数组就行了。

#include<iostream>
#include<cstring>
using namespace std;

int dp1[300][300],dp2[300][300];
int a[300];
int sum[300];

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=n+1;i<=2*n-1;i++)//化环为链
        a[i]=a[i-n];
    for(int i=1;i<=2*n-1;i++)
        sum[i]=sum[i-1]+a[i];//sum[i]=a[1]到a[i]的和
    memset(dp1,0,sizeof(dp1));//计算最大值
    memset(dp2,0x3f3f3f3f,sizeof(dp2));//计算最小值
    for(int i=1;i<=2*n-1;i++)
        dp2[i][i]=0;
    for(int len=2;len<=n;len++)//len-1 为区间长度
    {
        for(int i=1;i+len-1<=2*n-1;i++)//环转链 上限为2*n-1
        {
            int j=i+len-1;//左端点值+区间长度
            for(int k=i;k<j;k++)
            {
                dp1[i][j]=max(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[j]-sum[i-1]);
                dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    int MIN=0x3f3f3f3f;
    int MAX=0;
    for(int i=1;i<=n;i++)
    {
        MIN=min(MIN,dp2[i][i+n-1]);
        MAX=max(MAX,dp1[i][i+n-1]);
    }
    cout<<MIN<<endl<<MAX<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/87631735