洛谷P1880 [NOI1995]石子合并(区间dp模板)

题目描述

在一个圆形操场的四周摆放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

题意:第一行给出一个数n,接着第二行给出n个数,每个数表示一堆石子数,要求只能每次选相邻的两堆石子合并,并将这两个数的和作为本次合并的得分,计算出将这n堆石子合并成1堆的最小得分和最大得分.

思路:根据题意,这是一道区间dp的模板题,要求最后区间最大或最小值就等价于其每个子区间最大或最小,所以我们可以用两个二维数组p[i][j]来分别记录i到j这个区间的最大得分和最小得分,因为每次要选不同的相邻的数,所以需要一个k作为每次分割两个相邻的数的分界点,每次合并操作可以看成是:i到k这个区间和(k+1)到j这个区间合并成i到j这个区间,根据每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分,因此可以得到状态转移方程:

p1[i][j]=max(p1[i][j],p1[i][k]+p1[k+1][j]+num);
p2[i][j]=min(p2[i][j],p2[i][k]+p2[k+1][j]+num);

其中,1<=i<=<=k<j<=N。

num表示从i到j石子个数的和。(用前缀和s[j]-s[i-1]即可得到num)

然后先枚举确定区间间隔(从1到n-1),间隔确定后进而开始枚举固定间隔的每个区间,枚举每个区间时再dp枚举每种分法并把最大/小的作为最终p[i][j]的结果,这样从小的子区间到总区间的顺序依次dp就可确定最终结果了(即:先子区间最优,然后即可得总区间最优

完整代码:

#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
using namespace std;
int p1[211][211],p2[211][211],n,a[211],s[211];
//p1[i][j]表示从i到j这个区间的最大得分,p2[i][j]表示从i到j这个区间的最小得分,s[i]来记录前缀和(即:编号从1到i的数的和)
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        a[i+n]=a[i];
    }
    for(int i=1;i<=n<<1;i++)//注意这里要用n<<1(即2*n),因为题意是个环状的数列,写成两倍展开的链状便于操作
        s[i]=s[i-1]+a[i];//记录前缀和
    for(int t=1;t<n;t++)//t表示区间间隔
    {
        for(int i=1,j=i+t;(i<n<<1)&&(j<n<<1);i++,j=i+t)//枚举1到n区间间隔为t的所有区间(t=1:1-2 2-3 3-4 t=2:1-3 2-4 3-5 4-6由此枚举的顺序可看出先求子区间最优,然后再据此来求总区间最优)
        {
            p2[i][j]=inf;//初始化为无穷大
            int num=s[j]-s[i-1];//用前缀和求i到j这个区间的石子数(即:两堆石子合并后的得分)
            for(int k=i;k<j;k++)//枚举每种以k为分界点的合并方案
            {
                p1[i][j]=max(p1[i][j],p1[i][k]+p1[k+1][j]+num);//i到j这个区间最大得分等于所有方案中的最大得分(每次方案的得分=i到k这个区间的最大得分+(k+1)到j这个区间的最大得分+i到k和(k+1)到j这两堆石子合并的得分)
                p2[i][j]=min(p2[i][j],p2[i][k]+p2[k+1][j]+num);//i到j这个区间最小得分等于所有方案中的最小得分(每次方案的得分=i到k这个区间的最小得分+(k+1)到j这个区间的最小得分+i到k和(k+1)到j这两堆石子合并的得分)
            }
        }
    }
    int maxn=-1,minn=inf;
    for(int i=1;i<=n;i++)
    {
        maxn=max(maxn,p1[i][i+n-1]);//取每种方案的最大者作为最终最大得分
        minn=min(minn,p2[i][i+n-1]);//取每种方案的最小者作为最终最小得分
    }
    cout<<minn<<"\n"<<maxn<<endl;
    return 0;
}
发布了89 篇原创文章 · 获赞 5 · 访问量 6684

猜你喜欢

转载自blog.csdn.net/Mr_Kingk/article/details/101862393