P1880石子合并

传送

这是一个年代久远的区间dp (好像以前培训的时候讲了,但是现在才想起来去A)

区间dp常用状态:

f[i][j]:以i为左端点,j为右端点的最优解

第一层循环枚举区间长度,第二层循环枚举起点,第三层枚举中间的断点

(貌似写到这里这个题就写完了)

特点:

问题能转换为两两合并的问题(such as 能量项链,石子合并),当然可以再bt一点弄分解问题

步入正题

我们设f[i][j]为合并i到j的最大得分,g[i][j]为最小得分(代码里会出现一些奇怪的东西)。要合并i到j,则对于i,j之间的一个k来说,肯定是先合并了i到k,再合并k+1到 j。

而且合并完了还要计算合并i到k与合并k+1到j的得分。得分就是s[i](第i堆石子的个数)+s[i+1]+........+s[j],为了节省时间,我们用前缀和的形式计算,即sum[i]=s[1]+s[2]+...+s[i],那么得分也就是sum[j]-sum[i-1].        

综上,f[i][j]=max(f[i][k],f[k+1][j])+sum[j]-sum[i-1](i<=k<j)

 g[i][j]=min(g[i][k],g[k+1][j])+sum[j]-sum[i-1](i<=k<j)

我们再来考虑边界状态

f[i][i]=0,g[i][i]=0,因为合并i到i相当于没有合并,不得分

g[i][j]=INF,(取min用),f[i][j]=0(取max用)

这个题还牵扯到环,so这时候就该扯扯环变链了

因为n与1相邻,所以我们不妨把s[n+1]设成与s[1]一样,这样就可以处理合并n和1了。

但是合并n到2,3,4...n-1呢?(注意不是2,3,4...n-1到n)

思考过后,我们发现可以使s[n+i]=s[i],这样就可以处理起点是n的情况了

这样就可以写代码了(循环顺序在开头)

奇怪的变量名警告

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,s[209],mitsu[209][209],nobu[209][209],sum[209];//nobu是最大值,mitsu是最小值
int read()//一堆快读
{
    int x=0;
    bool flag=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')flag=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    if(flag)x=-x;
    return x;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        s[i]=read();s[i+n]=s[i];//环变链处理
    }
    for(int i=1;i<=2*n;i++)
    {
        sum[i]=sum[i-1]+s[i];
        nobu[i][i]=0;
        mitsu[i][i]=0;
    }
    for(int len=1;len<=n;len++)
    {
        for(int st=1;st<2*n-len+1;st++)
        {
            int end=st+len;
            mitsu[st][end]=214748364;
            for(int k=st;k<end;k++)
            {
                nobu[st][end]=max(nobu[st][end],nobu[st][k]+nobu[k+1][end]);
                mitsu[st][end]=min(mitsu[st][end],mitsu[st][k]+mitsu[k+1][end]);
            }
            nobu[st][end]+=sum[end]-sum[st-1];
            mitsu[st][end]+=sum[end]-sum[st-1];
        }
    }
    int maxn=-1,minn=214748364;
    for(int i=1;i<=n;i++)//统计答案
     {
       maxn=max(maxn,nobu[i][i+n-1]);
       minn=min(minn,mitsu[i][i+n-1]);
     }
     printf("%d\n%d",minn,maxn);
}
//I love Akchi Mitsuhide and Oda Nobu Naga

另一道区间DP典型题:能量项链

 

posted @ 2019-06-24 08:38  千载煜  阅读(344)  评论(0编辑  收藏  举报