zoukankan      html  css  js  c++  java
  • 洛谷P1973 [NOI2011]Noi嘉年华(动态规划,决策单调性)

    洛谷题目传送门

    DP题怕是都要大大的脑洞。。。。。。

    首先,时间那么大没用,直接离散化。

    第一问还好。根据题意容易发现,当一堆活动的时间有大量重叠的时候,更好的办法是把它们全部安排到一边去。那么我们转移的时候也肯定是要一块一块地转移啦。

    (tot_{l,r})为完全被包含在(l-r)时间内活动总数,直接(O(n^3))暴力求就好了。

    (pre_{i,j})为时间(1-i)内一边选(j)个时,另一边能选的最大值。枚举一块转移的话,我们的方程应该写成这样:

    [pre_ {i,j}=maxlimits_{k=1}^i{pre_ {k,j}+tot_{k,i},pre _{k,j-tot _{k,i}}} ]

    (显然两种情况都要考虑)

    然后答案就是(maxlimits_{j=1}^n{min(pre_{m,j},j)})啦((m)为离散化后的时间总长,不会超过(2n)

    这个数组为什么要叫(pre)呢?这是个前缀DP值。为了第二问,我们还要做个后缀DP,(suf_{i,j})表示时间(i-m)内一边选(j)个时,另一边能选的最大值,跟(pre)几乎一样的转移,也是(O(n^3))的。

    对于第二问,我们显然可以肯定(s_i-t_i)之内的活动都被一边选走了。至于(s_i)之前和(t_i)以后选了多少,我们也只好枚举。设(f_{l,r})为一边强制选(l-r)之间所有活动时最优的最小值,假定这一边在前面选了(x)个,在后面选了(y)个,另一边最多能选多少也就知道了,有方程

    [f_{l,r}=maxlimits_{x=1}^mmaxlimits_{y=1}^m{min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y})} ]

    然后第(i)个的答案就是(f_{s_i,t_i})么?注意千万别掉入这个误区!(pre)(suf)只是保证了局部最优,而没有保证全局最优。要说人话的话,就是可能有一个活动跨过了(s_i),然而(f_{s_i,t_i})并没有统计到它,只有扩大强制选的区间使得能够包含它,才能统计到最优解。于是需要枚举强制选区间了,(ans_i=maxlimits_{l=1}^{s_i}maxlimits_{r=t_i}^m{f_{l,r}})

    这样的话,整个(f)都必须要算出来,上面的枚举算法就变成(O(n^4))了,跑不动。

    点开标签发现有单调队列?!蒟蒻就往单调性上面想了想,于是就有了一个结论:设枚举(x)时有一个使答案最优的(y),那么当(x)增大时,如果(y)也增大那么答案不会更优。观察上面那个式子(min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y})),那么因为(pre,suf)都是递减的,所以很显然我们不能让(x,y)变大而(pre,suf)变小。

    于是,实现的时候,只要把(y)从大往小扫了,并不需要什么单调队列来维护它。

    #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
    ",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
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    HDU 4611 Balls Rearrangement 数学
    Educational Codeforces Round 11 D. Number of Parallelograms 暴力
    Knockout.Js官网学习(简介)
    Entity Framework 关系约束配置
    Entity Framework Fluent API
    Entity Framework DataAnnotations
    Entity Framework 系统约定配置
    Entity Framework 自动生成CodeFirst代码
    Entity Framework CodeFirst数据迁移
    Entity Framework CodeFirst尝试
  • 原文地址:https://www.cnblogs.com/flashhu/p/9469779.html
Copyright © 2011-2022 走看看