题目描述 Description
NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办。每个嘉年华可能包含很多个活动,而每个活动只能在一个嘉年华中举办。
现在嘉年华活动的组织者小安一共收到了 n个活动的举办申请,其中第 i 个活动的起始时间为 Si,活动的持续时间为Ti。这些活动都可以安排到任意一个嘉年华的会场,也可以不安排。
小安通过广泛的调查发现,如果某个时刻,两个嘉年华会场同时有活动在进行(不包括活动的开始瞬间和结束瞬间),那么有的选手就会纠结于到底去哪个会场,从而变得不开心。所以,为了避免这样不开心的事情发生,小安要求不能有两个活动在两个会场同时进行(同一会场内的活动可以任意进行)。
另外,可以想象,如果某一个嘉年华会场的活动太少,那么这个嘉年华的吸引力就会不足,容易导致场面冷清。所以小安希望通过合理的安排,使得活动相对较少的嘉年华的活动数量最大。
此外,有一些活动非常有意义,小安希望能举办,他希望知道,如果第i 个活动必须举办(可以安排在两场嘉年华中的任何一个),活动相对较少的嘉年华的活动数量的最大值。
输入描述 Input Description
输入的第一行包含一个整数 n,表示申请的活动个数。
接下来 n 行描述所有活动,其中第 i 行包含两个整数 Si、Ti,表示第 i 个活动从时刻Si开始,持续 Ti的时间。
输出描述 Output Description
输出的第一行包含一个整数,表示在没有任何限制的情况下,活动较少的嘉年华的活动数的最大值。
接下来 n 行每行一个整数,其中第 i 行的整数表示在必须选择第 i 个活动的前提下,活动较少的嘉年华的活动数的最大值。
样例输入 Sample Input
5
8 2
1 5
5 3
3 2
5 3
样例输出 Sample Output
2
2
1
2
2
2
数据范围及提示 Data Size & Hint
对于10%的数据,
1<=n<=10
1
<=
n
<=
10
对于30%的数据,
1<=n<=40
1
<=
n
<=
40
对于100%的数据,
1<=n<=200,0<=Si<=109,1<=Ti<=109
1
<=
n
<=
200
,
0
<=
S
i
<=
10
9
,
1
<=
T
i
<=
10
9
Solution
一开始看岔眼了,以为是一道傻*题(详见YBT活动选择),结果发现原来一个会场能同时举办多个活动…
显然离散化是必须的,毕竟只有相对时间有作用
然后就可以以时间来作为下标DP了
获取全局最优解比较简单, f[i][j] f [ i ] [ j ] 表示到 i i 时刻A会场举行场活动时B会场最多进行多少活动
转移方程很好推:
其中 sum s u m 数组代表从 [l,r] [ l , r ] 这段时间内的活动总和
max m a x 函数前半部分指的是
k k 时刻A会场举行场活动时B会场最多举行的活动数+ k k 到这段时间内的活动数
也就是将 k k 到这段时间内的活动全在B会场举行
max
m
a
x
函数后半部分同理
表示,
k
k
时刻A会场举行场活动时B会场最多举行的活动数
也就是将
k
k
到这段时间内的活动全在A会场举行
最后的答案就是 max(min(j,f[n][j])) m a x ( m i n ( j , f [ n ] [ j ] ) )
然后题目还要求必定选择某个特定的活动
其实将原方程变一下
正取一个
f
f
(pre),倒取一个(nxt)
pre[i][j]
p
r
e
[
i
]
[
j
]
表示从1到
i
i
中A会场举行场活动B会场能举行多少场活动
nxt[i][j]
n
x
t
[
i
]
[
j
]
表示从
i
i
到结尾中A会场举行场活动B会场能举行多少场活动
然后枚举中间部分(num),再加上左右区间就好了
为什么程序中貌似直接将num[i][j]给了B会场呢?其实是因为A,B会场是等价的…
可以试试看将num[i][j]直接给A会场,也是能AC的
具体看代码吧
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=205;
const int INF=1e9;
int b[N],e[N];
int s[N<<1];
int pre[N<<1][N],nxt[N<<1][N];
int num[N<<1][N<<1];
int mus[N<<1][N<<1];
int ANS[N];
int n;
int read() {
int ans=0,flag=1;
char ch=getchar();
while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if(ch=='-') { flag=-1;ch=getchar(); }
while(ch>='0' && ch<='9') { ans=ans*10+ch-'0';ch=getchar(); }
return ans*flag;
}
int main() {
n=read();
for(int i=1;i<=n;++i) {
b[i]=read();
e[i]=b[i]+read();
s[i*2-1]=b[i];
s[i*2]=e[i];
}
sort(s+1,s+n*2+1);
s[0]=unique(s+1,s+n*2+1)-(s+1);
for(int i=1;i<=n;++i) {
b[i]=lower_bound(s+1,s+s[0]+1,b[i])-s;
e[i]=lower_bound(s+1,s+s[0]+1,e[i])-s;
}
for(int i=1;i<=s[0];++i)
for(int j=i;j<=s[0];++j)
for(int k=1;k<=n;++k)
if(b[k]>=i && e[k]<=j)
++num[i][j];
for(int t=1;t<=s[0];++t)
for(int i=1;i<=n;++i)
pre[t][i]=nxt[t][i]=-1;
pre[0][0]=nxt[s[0]+1][0]=0;
for(int t=1;t<=s[0];++t) {
for(int i=0;i<=num[1][t];++i) pre[t][i]=max(pre[t-1][i],pre[t][i]);//继承上面答案
for(int i=0;i<=num[1][t];++i)
for(int j=1;j<t;++j) {
if(i>=num[j][t])
pre[t][i]=max(pre[j][i-num[j][t]],pre[t][i]);//num[j][t]都由1会场拿到
if(pre[j][i]!=-1)
pre[t][i]=max(pre[j][i]+num[j][t],pre[t][i]);//num[j][t]都由2会场拿到
}
}
for(int t=s[0];t;--t) {
for(int i=0;i<=num[t][s[0]];++i) nxt[t][i]=max(nxt[t+1][i],nxt[t][i]);//继承上面答案
for(int i=0;i<=num[t][s[0]];++i)
for(int j=s[0];j>t;--j) {
if(i>=num[t][j])
nxt[t][i]=max(nxt[j][i-num[t][j]],nxt[t][i]);//num[t][j]都由1会场拿到
if(nxt[j][i]!=-1)
nxt[t][i]=max(nxt[j][i]+num[t][j],nxt[t][i]);//num[t][j]都由2会场拿到
}
}
for(int i=1;i<=s[0];++i)
for(int j=i+1;j<=s[0];++j) {
for(int x=0,y=n;x<=n;++x) {
if(pre[i][x]==-1) continue;
while(y) {
int val1=min(x+y,pre[i][x]+num[i][j]+nxt[j][y]);
int val2=min(x+y-1,pre[i][x]+num[i][j]+nxt[j][y-1]);
if(val2>=val1 || nxt[j][y]==-1) --y;
else break;
}
mus[i][j]=max(mus[i][j],min(x+y,pre[i][x]+num[i][j]+nxt[j][y]));//必选num[i][j]到2会场的答案
//也可以改成mus[i][j]=max(mus[i][j],min(x+y+num[i][j],pre[i][x]+nxt[j][y]));
//当然,如果你要改这一句的话上面val1,val2也需要改
}
}
for(int i=0;i<=num[1][s[0]];++i)
ANS[0]=max(ANS[0],min(pre[s[0]][i],i));
for(int i=1;i<=n;++i)
for(int l=b[i];l>=1;--l)
for(int r=e[i];r<=s[0];++r)
ANS[i]=max(ANS[i],mus[l][r]);
for(int i=0;i<=n;i++) printf("%d\n",ANS[i]);
return 0;
}