- 传送门 -
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;
}