zoukankan      html  css  js  c++  java
  • bzoj 2436: [Noi2011]Noi嘉年华

    Description

    NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办。每个嘉年华可能包含很多个活动,而每个活动只能在一个嘉年华中举办。
    现在嘉年华活动的组织者小安一共收到了 n个活动的举办申请,其中第 i 个活动的起始时间为 Si,活动的持续时间为Ti。这些活动都可以安排到任意一个嘉年华的会场,也可以不安排。
    小安通过广泛的调查发现,如果某个时刻,两个嘉年华会场同时有活动在进行(不包括活动的开始瞬间和结束瞬间),那么有的选手就会纠结于到底去哪个会场,从而变得不开心。所以,为了避免这样不开心的事情发生,小安要求不能有两个活动在两个会场同时进行(同一会场内的活动可以任意进行)。
    另外,可以想象,如果某一个嘉年华会场的活动太少,那么这个嘉年华的吸引力就会不足,容易导致场面冷清。所以小安希望通过合理的安排,使得活动相对较少的嘉年华的活动数量最大。
    此外,有一些活动非常有意义,小安希望能举办,他希望知道,如果第i 个活动必须举办(可以安排在两场嘉年华中的任何一个),活动相对较少的嘉年华的活动数量的最大值。

    Solution

    正解:DP+单调性优化
    第一问非常简单,有两个嘉年华,所以要固定一个,设 (f[i][j]) 表示目前在 (i) 这个活动的右端点上,选择了(j)个放入第一个嘉年华,第二个嘉年华的最大活动数量
    转移有两种:枚举 (k<i)(k)(i)之间活动的要不给第一个嘉年华要不给第二个,所以预处理一个 (c[i][j]) 表示 (i)(j)时间段内的活动数量,就可以 (O(1)) 转移了

    第二问可以直接用第一问的DP数组,同理再求一个后缀的 (f) 数组,设为 (g).
    那么如果是固定时间在 (i,j) 之间的活动的话,那么就是从 (f[i-1][a]+g[j+1][b]+c[i][j]) 中决策了,枚举(a,b),讨论(c[i][j])分给第一个嘉年华,分给第二个嘉年华即可.
    这样做的话是 (O(n^5)),所以只需要离线询问,设 (dp[i][j]) 表示上面说的:固定时间在 (i,j) 之间的活动,两个嘉年华中的最小值的最大值为多少
    回答询问就可以 O((n^2)) 了.

    注意:预处理 (dp) 数组是 (O(n^4)),但是限制上界的话,暴力是可以过的.
    正解是 (O(n^3)) 预处理的,利用一个单调性:
    假设枚举(a)为左边分给第一个嘉年华的活动数量,(b)为右边分给第一个嘉年华的
    那么 (a)增大,(b)只能减小,所以可以单调指针扫描 (b),复杂度就降下来了.

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=405;
    int n,B[N],num=0,c[N][N],f[N][N],g[N][N],dp[N][N],m;
    struct sub{int l,r;}e[N];
    void work()
    {
    	scanf("%d",&n);m=n;
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&e[i].l,&e[i].r);
    		e[i].r=e[i].l+e[i].r-1;
    		B[++num]=e[i].l;B[++num]=e[i].r;
    	}
    	sort(B+1,B+num+1);
    	num=unique(B+1,B+num+1)-B-1;
    	for(int i=1;i<=m;i++){
    		e[i].l=lower_bound(B+1,B+num+1,e[i].l)-B;
    		e[i].r=lower_bound(B+1,B+num+1,e[i].r)-B;
    		for(int j=e[i].l;j>=1;j--)
    			for(int k=e[i].r;k<=num;k++)c[j][k]++;
    	}
    	memset(f,-127/3,sizeof(f));
    	memset(g,-127/3,sizeof(g));
    	n=num;
    	f[0][0]=0;
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<=c[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]+c[k+1][i]);
    		if(j>=c[k+1][i])f[i][j]=Max(f[i][j],f[k][j-c[k+1][i]]);
    			}
    		}
    	g[n+1][0]=0;
    	for(int i=n;i>=1;i--)
    		for(int j=0;j<=c[i][n];j++){
    			g[i][j]=g[i+1][j];
    			for(int k=i+1;k<=n+1;k++){
    			g[i][j]=Max(g[i][j],g[k][j]+c[i][k-1]);
    		if(j>=c[i][k-1])g[i][j]=Max(g[i][j],g[k][j-c[i][k-1]]);
    			}
    		}
    	int ans=0;
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<=i;j++)
    			ans=max(ans,min(f[i][j],j));
    	printf("%d
    ",ans);
    	int v1,v2,b;
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++){
    			b=c[j+1][n];
    			for(RG int a=0;a<=c[1][i-1];a++){
    				for(;b>=0;b--){
    				v1=min(a+b,f[i-1][a]+g[j+1][b]+c[i][j]);
    				v1=max(v1,min(a+b+c[i][j],f[i-1][a]+g[j+1][b]));
    				v2=min(a+b-1,f[i-1][a]+g[j+1][b-1]+c[i][j]);
    			v2=max(v2,min(a+b+c[i][j]-1,f[i-1][a]+g[j+1][b-1]));
    				if(v1>v2)break;
    				}
    				dp[i][j]=Max(dp[i][j],v1);
    			}
    		}
    	for(int i=1;i<=m;i++){
    		ans=0;
    		for(int j=e[i].l;j>=1;j--)
    			for(int k=e[i].r;k<=n;k++)
    				ans=max(ans,dp[j][k]);
    		printf("%d
    ",ans);
    	}
    }
    
    int main()
    {
    	freopen("pp.in","r",stdin);
    	freopen("pp.out","w",stdout);
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    MySQL学习笔记(六):索引
    正则表达式基础知识,持续更新…
    js改变盒子大小(上下左右)分析
    表单自定义样式
    js拖拽分析
    javascript右键菜单分析
    简要分析javascript的选项卡和轮播图
    表单联动的总结
    浅显总结ASCII Unicode UTF-8的区别
    瀑布流知识的延伸
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8149094.html
Copyright © 2011-2022 走看看