P1880 [NOI1995]石子合并

  这竟然是一道区间dp……一开始我按照普通dp做的……于是咳咳……

  我一开始枚举左端点 i ,右端点 j ,在枚举中间的任意一个 k ,一直取最大值,但是 “ 显然不对 ” 。

  那为什么呢?

  就是因为:

  在处理 i 到 j 的时候,我们并不知道 k 到 j 的最优解,所以这次我们就没用到本应该用到的值来更新现有解,自然不对。

  这就需要区间dp了,先枚举长度,这样所有小的长度都会被提前修改,这样就保证了以后用到的都是最优解。

  再用前缀和优化一下,每一次 i 到 j 都要加上 i 到 j 的果子总数,因为相当于进行了一次合并,而里面现在具体进行的合并,就是那个 f [ x ] [ y ] ,在我们枚举小长度的时候,就已经加过一遍二者果子数之和了,现在可以直接用。(不太好解释,感性理解一下)

  下面附上代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 2000
int n,val[maxn],pl[maxn],num[maxn];
int fax[maxn][maxn],fin[maxn][maxn];
int find(int a,int b)
{
    return pl[b]-pl[a-1];
}
int main()
{
    int ans=0,ANS=9999999999;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&num[i]);
        pl[i]=pl[i-1]+num[i];
        num[i+n]=num[i];
    }
    for(int i=n+1; i<=n*2; i++)
        pl[i]=pl[i-1]+num[i];
    for(int p=1; p<n; p++)
        for(int i=1,j=p+i; i<2*n&&j<=2*n; i++,j=p+i)
        {
            fin[i][j]=999999999;
            for(int k=i; k<j; k++)
            {
                fax[i][j]=max(fax[i][j],fax[i][k]+fax[k+1][j]+find(i,j));
                fin[i][j]=min(fin[i][j],fin[i][k]+fin[k+1][j]+find(i,j));
            }
        }
    for(int i=1; i<=n+1; i++)
    {
        ans=max(ans,fax[i][i+n-1]);
        ANS=min(ANS,fin[i][i+n-1]);
    }
    printf("%d\n%d",ANS,ans);
    return 0;
}

 

posted @ 2019-01-28 20:11  paopo  阅读(127)  评论(0编辑  收藏  举报