[BZOJ2436][Noi2011]Noi嘉年华(dp)

题目描述

传送门

题解

非常神的一道dp题,感觉在考场上想出来是十分困难的。

首先对于第一问,先将区间离散,那么所有的时间就都变成 O(n) 级别的了。可以预处理出 sum[i][j] 表示时间i~j内有多少个活动。设 f[i][j] 表示到第i时间为止,第一个场地已经举办了j场活动时第二个场地最多举办多少场活动。
求f的过程可以用一个 O(n3) 的dp,转移方程为 f[i][j]=max{f[i1][j],f[k][j]+sum[k+1][i],f[k][jsum[k+1][i]]}
分别表示不举行活动,在二号场地举行活动和在一号场地举行活动。
那么第一问的答案就是 max{min(i,f[time][i])}

对于第二问,再用同样的方法求 g[i][j] 表示倒序时间到i时,第一个场地举办了j个活动时第二个场地最多举办多少个活动。
dp[i][j] 表示i~j这一段所有的活动同时选到某一个场地的最大答案。
显然如果我们必须选的是区间(l,r)
那答案就是 max{dp[i][j](i<=x,j>=y)}

dp[i][j] 怎么求?
dp[i][j]=max{min(min(x+y,f[i1][x]+g[j+1][y])+sum[i][j],max(x+y,f[i1][x]+g[j+1][y]))}
这样复杂度是 O(n4) .

考虑如何优化。
我们假设随着x上升时,y也增加,那么显然 f[i1][x] g[j+1][y] 都是不增的。从要使 x+y f[i1][x]+g[j+1][y] ,尽量平均的角度考虑,这样的情况是不可能出现的。
所以我们得出了一个结论:随着x上升,在最优处的y值是单调不增的。
时间复杂度可以优化到 O(n3) .

代码

#include
#include
#include
#include
using namespace std;
#define N 205

int n,s,t,xx,lsh,ans,inf;
int X[N*2],p[N*2],loc[N*2],sum[N*2][N*2],f[N*2][N],g[N*2][N],dp[N*2][N*2];
struct hp{int l,r;}show[N];

inline int cmp(int a,int b)
{
    return X[a]inline int get(int i,int j,int x,int y)
{
    if (f[i-1][x]==inf||g[j+1][y]==inf) return inf;
    return min(min(x+y,f[i-1][x]+g[j+1][y])+sum[i][j],max(x+y,f[i-1][x]+g[j+1][y]));
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%d%d",&s,&t);
        X[++xx]=s,X[++xx]=s+t-1;
    }
    for (int i=1;i<=xx;++i)  p[i]=i;
    sort(p+1,p+xx+1,cmp);X[0]=-1;
    for (int i=1;i<=xx;++i)
        if (X[p[i]]!=X[p[i-1]]) loc[p[i]]=++lsh;
        else loc[p[i]]=lsh;
    xx=0;
    for (int i=1;i<=n;++i)
        show[i].l=loc[++xx],show[i].r=loc[++xx];
    for (int i=1;i<=lsh;++i)
        for (int j=i;j<=lsh;++j)
            for (int k=1;k<=n;++k)
                if (show[k].l>=i&&show[k].r<=j)
                    sum[i][j]++;
    memset(f,128,sizeof(f));
    inf=f[0][0];
    f[0][0]=0;
    for (int i=1;i<=lsh;++i)
        for (int j=0;j<=sum[1][i];++j)
        {
            f[i][j]=f[i-1][j];
            for (int k=0;k1][i]);
                if (j-sum[k+1][i]>=0) f[i][j]=max(f[i][j],f[k][j-sum[k+1][i]]);
            }
        }

    ans=0;
    for (int i=0;i<=n;++i)
        ans=max(ans,min(i,f[lsh][i]));
    printf("%d\n",ans);

    memset(g,128,sizeof(g));
    g[lsh+1][0]=0;
    for (int i=lsh;i>=1;--i)
        for (int j=0;j<=n;++j)
        {
            g[i][j]=g[i+1][j];
            for (int k=lsh+1;k>i;--k)
            {
                g[i][j]=max(g[i][j],g[k][j]+sum[i][k-1]);
                if (j-sum[i][k-1]>=0) g[i][j]=max(g[i][j],g[k][j-sum[i][k-1]]);
            }
        }
    for (int i=1;i<=lsh;++i)
        for (int j=i;j<=lsh;++j)
        {
            int y=n;
            for (int x=0;x<=n;++x)
            {
                int now=get(i,j,x,y);
                while (y&&now<=(t=get(i,j,x,y-1))){now=t; y--;}
                dp[i][j]=max(now,dp[i][j]);
            }
        }

    for (int k=1;k<=n;++k)
    {
        ans=0;
        for (int i=1;i<=show[k].l;++i)
            for (int j=show[k].r;j<=lsh;++j)
                ans=max(ans,dp[i][j]);
        printf("%d\n",ans);
    }

}

总结

你可能感兴趣的:(题解,dp,NOI)