• [题解] bzoj 2436 Noi 2011 NOI嘉年华(DP)


    - 传送门 -

     http://www.lydsy.com/JudgeOnline/problem.php?id=2436
     

    2436: [Noi2011]Noi嘉年华

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 705  Solved: 467
    [Submit][Status][Discuss]

    Description

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

    Input

    输入的第一行包含一个整数 n,表示申请的活动个数。 
    接下来 n 行描述所有活动,其中第 i 行包含两个整数 Si、Ti,表示第 i 个活
    动从时刻Si开始,持续 Ti的时间。

    Output

    输出的第一行包含一个整数,表示在没有任何限制的情况下,活动较少的嘉
    年华的活动数的最大值。 
    接下来 n 行每行一个整数,其中第 i 行的整数表示在必须选择第 i 个活动的
    前提下,活动较少的嘉年华的活动数的最大值。

    Sample Input

    5
    8 2
    1 5
    5 3
    3 2
    5 3

    Sample Output

    2
    2
    1
    2
    2
    2

    HINT

    在没有任何限制的情况下,最优安排可以在一个嘉年华安排活动 1, 4,而在

    另一个嘉年华安排活动 3, 5,活动2不安排。

    1≤n≤200 0≤Si≤10^9

    1≤Ti≤ 10^9

      ### - 题意 -  把节目当成一根线段 [ s , t ].  从 n 条线段中取出若干条, 在不切断线段的情况下将取出的线段切成若干份, 再将这若干份线段集分为两份, 使少的一份分到线段最多.  考虑必须取第 1 ~ n 条线段的情况.    ### - 思路 -  先用离散化处理数据, 设 $l_{ij}$ 表示前 i 个点 A 取 j 条线段之后 B 的最大数量, $r_{ij}$ 表示 i 点到右端点 A 取 j 条线段之后 B 的最大数量, $c_{ij}$ 表示 i 到 j 中线段数量, $g_{ij}$ 表示 i 到 j 内线段全都被 B 取到时的答案 .    $l_{ij}$ = max{ max{ $l_{kj}+c_{ki}$, $l_{k j-c_{ki}}$ } 0<=k <=i }   (前者表示 A 在 [0,k] 范围内取 j 条时 B 的最大取值 加上 [k,i] 的线段数,    后者表示 A 在 [0,k] 范围内取 $j-c_{ki}$ 条, 再取 [k,i] 范围内全部 $c_{ki}$ 条, 此时 B 的最大取值.)    r同理.     $g_{ij}$ = max { min{ x+y, $l_{ix}+c_{ij}+r_{jy}$ } 0<=x,y<=n }    ( A 在 [0, i] 中取 x 条线段,[j, 右端] 中取 y 条.)  枚举 x, y 的复杂度 (加上枚举i,j) 是 O(n^4)的, 但我们考虑: 假设 x+y 是较小值, 那当 x 增加时 y 必定要减小, 这样我们就可以只枚举 x, 维护 y 单调递减就好了, 复杂度降至O(n^3).

     细节见代码.

     PS :
     依旧窃别人的AC代码, 但我竟然没窃成功!!!
     于是愉快地找了一下午不同最后发现只有数组顺序不同
     依旧问Dalao , Dalao J 里 J 气地 说:
     我一眼就看出来--
     是数组溢出
     ....咦???
     是的, 这份AC代码在运行时数组下标竟然钻出来一个负数!!!
     可是这玄学代码还过了!!!
     原因是该AC代码数组顺序没毛病而我的顺序太玄学所以挂了,于是在访问数组时加特判看数组下标是否出现负数...
      

    - 代码 -

     

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define mem(a,b) memset(a,b,sizeof a)
    #define FOR(i,a,b) for(int i = a; i <= b; i++)
    #define PRE(i,a,b) for(int i = a; i >= b; i--)
    using namespace std;
    
    int l[405][205],r[405][205],g[405][405];
    int s[205],t[205],c[405][405],a[405];
    int m,n;
    
    int main() {
    	scanf("%d",&n);
    	FOR(i,1,n) {
    		scanf("%d%d",&s[i],&t[i]);
    		t[i] += s[i];
    		a[++m] = s[i]; a[++m] = t[i];
    	}
    	sort(a+1,a+m+1);
    	m = unique(a+1,a+m+1)- a - 1;
    	FOR(i,1,n)
    		s[i] = lower_bound(a+1,a+m+1,s[i]) - a,
    		t[i] = lower_bound(a+1,a+m+1,t[i]) - a; //离散化
    	FOR(i,0,m) {
    		FOR(j,1,n)
    			if(s[j] >= i)
    				c[i][t[j]]++; // 此时c[i][j]表示[i,j]中以j结尾的线段条数
    		FOR(j,i+2,m)
    			c[i][j] += c[i][j-1];	 // [i,j]中线段条数
    	}
    	mem(l,128); mem(r,128); mem(g,128);
    	l[0][0] = 0;
    	FOR(i,1,m) FOR(j,0,n)
    		FOR(k,0,i-1) {
    			l[i][j] = max(l[i][j],l[k][j]+c[k][i]);
    			if(j-c[k][i] >= 0) l[i][j] = max(l[i][j],l[k][j-c[k][i]]); //防止下标出现负数
    	}
    	r[m][0] = 0;
    	PRE(i,m-1,0) FOR(j,0,n)
    		PRE(k,m,i+1) {
    			r[i][j] = max(r[i][j],r[k][j]+c[i][k]);
    			if(j-c[i][k] >= 0) r[i][j] = max(r[i][j],r[k][j-c[i][k]]);
    	}
    	
    
    	FOR(i,0,m) FOR(j,i+1,m) {
    		for(int x = 0,y = n; x <= n; x++) {
    			while(y >= 0 && x + y > l[i][x] + c[i][j] + r[j][y]) y--; // 保证x+y为较小值, 当 x 增大时, 另一方取到线段数明显减少, 故要减小y
    			if(y >= 0) g[i][j] = max(g[i][j], x+y);
    			else if(l[i][x]>=0 && r[j][y]>=0 ) g[i][j] = max(g[i][j],l[i][x] + c[i][j] + r[j][y]);
    		}
    	}
    	int ans = 0;
    	FOR(i,0,n)
    		ans = max(ans,min(l[m][i],i));
    	printf("%d
    ",ans);
    	
    	FOR(i,1,n) {
    		ans = 0;
    		FOR(x,0,s[i]) FOR(y,t[i],m)
    		ans = max(ans,g[x][y]);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }   
    
  • 相关阅读:
    奥运圣火在家乡传递
    Please stop reinventing the wheel (请不要重复发明轮子)
    使用IDispatch::Invoke函数在C++中调用C#实现的托管类库方法
    To invoke and to begin invoke, that is a question.
    XML和JSON(JavaScript Object Notation)
    Cloud Computing Is a Big Whiteboard
    TRIE Data Structure
    ASP.NET AJAX UpdatePanel 控件实现剖析
    分布式计算、网格计算和云计算
    系统架构设计师考试大纲(2009版)
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7147675.html
Copyright © 2020-2023  润新知