题解 P1973 【[NOI2011]Noi嘉年华】
DP题怕是都要大大的脑洞。。。。。。
首先,时间那么大没用,直接离散化。
第一问还好。根据题意容易发现,当一堆活动的时间有大量重叠的时候,更好的办法是把它们全部安排到一边去。那么我们转移的时候也肯定是要一块一块地转移啦。
设
设
(显然两种情况都要考虑)
然后答案就是
这个数组为什么要叫
对于第二问,我们显然可以肯定
然后第
这样的话,整个
点开标签发现有单调队列?!蒟蒻就往单调性上面想了想,于是就有了一个结论:设枚举
于是,实现的时候,只要把
#include<cstdio>
#include<algorithm>
#define RG register
#define R RG int
#define G c=getchar()
#define Upd(A,L,R) {chkmx(A[i][j],A[k][j]+tot[L][R]); \
if(j>=tot[L][R])chkmx(A[i][j],A[k][j-tot[L][R]]);}
#define Calc(y) min(x+tot[l][r]+y,pre[l][x]+suf[r][y])
using namespace std;
const int N=209,M=409,INF=1e9;
int s[N],t[N],b[M],tot[M][M],pre[M][N],suf[M][N],f[M][M];
inline int in(){
RG char G;
while(c<'-')G;
R x=c&15;G;
while(c>'-')x=x*10+(c&15),G;
return x;
}
inline int min(R x,R y){return x<y?x:y;}
inline void chkmx(R&x,R y){if(x<y)x=y;}
int main(){
R n=in(),m=0,i,j,k,l,r,x,y,p0,p1,ans;
for(i=1;i<=n;++i){
b[++m]=s[i]=in();
b[++m]=t[i]=in()+s[i];
}
sort(b+1,b+m+1);//离散化
m=unique(b+1,b+m+1)-b-1;
for(i=1;i<=n;++i){
s[i]=lower_bound(b+1,b+m+1,s[i])-b;
t[i]=lower_bound(b+1,b+m+1,t[i])-b;
for(l=1;l<=s[i];++l)//tot暴力求
for(r=m;r>=t[i];--r)++tot[l][r];
}
for(i=1;i<=m;++i)//注意初始化
for(j=1;j<=n;++j)pre[i][j]=suf[i][j]=-INF;
for(i=1;i<=m;++i)
for(j=0;j<=tot[1][i];++j)
for(k=1;k<=i;++k)Upd(pre,k,i);
for(i=m;i;--i)//转移很相似,搞了个宏定义
for(j=0;j<=tot[i][m];++j)
for(k=i;k<=m;++k)Upd(suf,i,k);
for(l=1;l<=m;++l)
for(r=l+1;r<=m;++r)
for(y=n,x=0;x<=n;++x){
p0=Calc(y);//p0为最优决策,p1为当前决策
while(y&&p0<=(p1=Calc(y-1)))p0=p1,--y;
chkmx(f[l][r],Calc(y));
}
ans=0;
for(j=1;j<=n;++j)chkmx(ans,min(pre[m][j],j));
printf("%d\n",ans);
for(i=1;i<=n;++i){
ans=0;
for(l=1;l<=s[i];++l)
for(r=m;r>=t[i];--r)chkmx(ans,f[l][r]);
printf("%d\n",ans);
}
return 0;
}