您的位置:首页 > 其它

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

2016-08-17 18:16 465 查看

题目描述

传送门

题解

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

首先对于第一问,先将区间离散,那么所有的时间就都变成O(n)级别的了。可以预处理出sum[i][j]表示时间i~j内有多少个活动。设f[i][j]表示到第i时间为止,第一个场地已经举办了j场活动时第二个场地最多举办多少场活动。

求f的过程可以用一个O(n3)的dp,转移方程为f[i][j]=max{f[i−1][j],f[k][j]+sum[k+1][i],f[k][j−sum[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[i−1][x]+g[j+1][y])+sum[i][j],max(x+y,f[i−1][x]+g[j+1][y]))}。

这样复杂度是O(n4).

考虑如何优化。

我们假设随着x上升时,y也增加,那么显然f[i−1][x]和g[j+1][y]都是不增的。从要使x+y和f[i−1][x]+g[j+1][y],尽量平均的角度考虑,这样的情况是不可能出现的。

所以我们得出了一个结论:随着x上升,在最优处的y值是单调不增的。

时间复杂度可以优化到O(n3).

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
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]
,g[N*2]
,dp[N*2][N*2];
struct hp{int l,r;}show
;

inline int cmp(int a,int b)
{
return X[a]<X[b];
}
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;k<i;++k)
{
f[i][j]=max(f[i][j],f[k][j]+sum[k+1][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);
}

}


总结

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: