P1880 [NOI1995] 石子合并

题目:

https://www.luogu.com.cn/problem/P1880

题目描述

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

试设计出一个算法,计算出将 NN 堆石子合并成 11 堆的最小得分和最大得分。

输入格式

数据的第 11 行是正整数 NN,表示有 NN 堆石子。

第 22 行有 NN 个整数,第 ii 个整数 a_iai 表示第 ii 堆石子的个数。

输出格式

输出共 22 行,第 11 行为最小得分,第 22 行为最大得分。

 

区间dp

把环形链破坏成两条首位相连的链,注意要把所有的都换成n+n:

第一维枚举子区间的长度

第二维枚举左端点

第三维枚举断点

(ps:不能枚举用只用两层循环来枚举左右端点,因为那样无法保证最优子结构,而上面的三重循环则从小区间到大区间过渡)

 

//石子合并,只能合并相邻的
//每次合并的得分是两堆石子的和
//先考虑无环的情况
//枚举断点k
//f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[i][j]);
//sum[i][j]用前缀和来写,=s[j]-s[i-1];
//如果有环,就破环成链,相当于把数列加倍
// 把环形链破坏成两条首位相连的链,注意要把所有的都换成n+n:

 

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
typedef pair<int ,int > p;
typedef long long ll;
int t,m;
int a[204];
int s[204];
int dp[204][204];
int d[204][204];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
           scanf("%d",&a[i]);
    s[i]=s[i-1]+a[i];
    }

    for(int i=1;i<=n;i++)
    {
        a[n+i]=a[i];
         s[n+i]=s[n+i-1]+a[i];
    }

    for(int i=1;i<=n;i++)
        s[i]=s[i-1]+a[i];
    for(int len=2;len<=n;len++)
    {
        for(int i=1,j=i+len-1;i<=n+n&&j<=n+n;i++,j=i+len-1)
        {
            for(int k=i;k<j;k++)
            {  dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
              if(d[i][j]==0)
                d[i][j]=d[i][k]+d[k+1][j]+s[j]-s[i-1];
              else
                d[i][j]=min(d[i][j],d[i][k]+d[k+1][j]+s[j]-s[i-1]);
            }

        }
    }
   int mmax=0;
   int mmin=0x3f3f3f3f;
   for(int i=1;i+n-1<=n+n;i++)
   {
       mmax=max(mmax,dp[i][i+n-1]);
       mmin=min(mmin,d[i][i+n-1]);
   }
   printf("%d\n",mmin);
      printf("%d\n",mmax);

}

 

posted @ 2021-07-29 10:14  废柴废柴少女  阅读(69)  评论(0)    收藏  举报