石子合并

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

一道区间dp题目。

用d[i][j]表示从i到j的最大/最小得分,那么依次枚举长度len,坐标i和j,三层循环就可以dp递推求得最值了(听说这是道NOI题目)

记得使用前缀和哦。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<ctype.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
    int x=0,w=1;char c=getchar();
    while(!isdigit(c)){
        if(c=='-')w=-1;
        c=getchar();
    }
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*w;
}
const int maxn=105;
int a[maxn],sum[maxn],d[maxn][maxn],d2[maxn][maxn];
int main()
{
    int n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];//前缀和 
    for(int len=2;len<=n;len++)
        for(int i=1;i<=n-len+1;i++)
        {
            int j=i+len-1;
            d[i][j]=INF;
            for(int k=i;k<j;k++)
            {
                if(d[i][j]>d[i][k]+d[k+1][j]+sum[j]-sum[i-1])
                d[i][j]=d[i][k]+d[k+1][j]+sum[j]-sum[i-1];
                if(d2[i][j]<d2[i][k]+d2[k+1][j]+sum[j]-sum[i-1])
                d2[i][j]=d2[i][k]+d2[k+1][j]+sum[j]-sum[i-1];
            }
                
        }
    printf("%d\n%d\n",d[1][n],d2[1][n]);
    return 0; 
}

勇于实践的小伙伴们可以惊讶的发现:它爆零了!

emmm我们仔细审题会发现这是一个环。

所以不要着急我们给它再加n个数复制一遍就可以AC了(发扬了dp用内存换速度的优良作风)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<ctype.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
    int x=0,w=1;char c=getchar();
    while(!isdigit(c)){
        if(c=='-')w=-1;
        c=getchar();
    }
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*w;
}
const int maxn=105*2;
int a[maxn],sum[maxn],d[maxn][maxn],d2[maxn][maxn];
int main()
{
    int n=read();
    for(int i=1;i<=n;i++)a[i]=a[i+n]=read();
    for(int i=1;i<=(n+n);i++)sum[i]=sum[i-1]+a[i];//前缀和 
    for(int len=2;len<=n;len++)
        for(int i=1,j=i+len-1;i<n+n and j<n+n ;i++,j=i+len-1)
        {
            d[i][j]=INF;
            for(int k=i;k<j;k++)
            {
                if(d[i][j]>d[i][k]+d[k+1][j]+sum[j]-sum[i-1])
                d[i][j]=d[i][k]+d[k+1][j]+sum[j]-sum[i-1];
                if(d2[i][j]<d2[i][k]+d2[k+1][j]+sum[j]-sum[i-1])
                d2[i][j]=d2[i][k]+d2[k+1][j]+sum[j]-sum[i-1];
            }
                
        }
    int ans1=INF,ans2=0;
    for(int i=1;i<=n;i++)ans1=min(ans1,d[i][i+n-1]),ans2=max(ans2,d2[i][i+n-1]);
    printf("%d\n%d\n",ans1,ans2);
    return 0; 
}

当然,这个直接把n<<1的作死做法是没有办法干过大多数题目的。于是我们就需要对dp进行平行四边形优化

但是我没学……以后学了单独写一篇题解吧。

扩展:这道题的数据范围是n<=100,而我们给它还扩充到了n<=200。那么如果n更大呢?比如n<=40000?

这里有一道题目洛谷P5569

是的我们没法再用40000*40000的dp做了。

我也不会

这已经不是dp的问题了!但是还是给大佬们一个传送门

posted @ 2020-06-08 22:14  Star_Cried  阅读(141)  评论(0编辑  收藏  举报