【bzoj 2436】[Noi2011] Noi嘉年华 (dp)

66 篇文章 0 订阅
7 篇文章 0 订阅

2436: [Noi2011]Noi嘉年华

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 527   Solved: 380
[ 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

[ Submit][ Status][ Discuss]

【题解】【dp+离散化】

【转移方程:f[i][j]=max{f[i-1][j],f[k][j]+sum[k+1][i],f[k][j-sum[k+1][i]]};dp[i][j]=max{min(min(f[i-1][x],g[j+1][y])+sum[i][j], max(f[i-1][x],g[j+1][y]))}】

【由于一个很友好的性质:dp的时候随着x的增大,dp[i][j]时的y是单调不增的。所以可以拿指针之类的扫一下,复杂度由O(n^4)降至O(n^3)】

【其中:f[i][j]表示到i时间,第一个场地已经进行了j场比赛,第二个场地最多进行了多少场比赛;

  g[i][j]表示倒序到i时间,第一个场地已经进行了j场比赛,第二个场地最多进行多少场比赛;

  dp[i][j]表示i-j时间内的表演都选在同一场地的最大答案;

  sum[i][j]表示i-j时间内共有多少场比赛】

<span style="font-size:18px;"><strong>#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct room{
	int s,t;
}a[510];
int b[510],n,tot,INF;//数组b用来离散化;
int f[510][510],g[510][510],sum[510][510],dp[510][510];//sum[i][j]表示i-j的时间里有多少活动;f[i][j]表示到i时间,第一个会场有j场演出,第二个会场最多有几场演出;g[i][j]是f的倒序求解;dp[i][j]表示i-j中只在一个会场演出最多有几场 
inline int get(int i,int j,int x,int y)
{
	if(f[i-1][x]==INF||g[j+1][y]==INF) return INF;
	int s1=x+y,s2=f[i-1][x]+g[j+1][y],s=sum[i][j];
	return max(min(s1,s2+s),min(s1+s,s2));
}
int main()
{
	freopen("show.in","r",stdin);
	freopen("show.out","w",stdout); 
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	a[i].s=b[++tot]=x; a[i].t=b[++tot]=x+y-1;
	 }
	sort(b+1,b+tot+1);
	int m=unique(b+1,b+tot+1)-b-1;
	for(int l=1;l<=n;++l)
	 {
	 	a[l].s=lower_bound(b+1,b+m+1,a[l].s)-b;
	 	a[l].t=lower_bound(b+1,b+m+1,a[l].t)-b;
	 	for(i=a[l].s;i>0;i--)
	 	 for(j=a[l].t;j<=m;++j)
	 	  sum[i][j]++;
	 }//初始化sum
	memset(f,128,sizeof(f));
	memset(g,128,sizeof(g));
	INF=f[0][0]; f[0][0]=g[m+1][0]=0;
	int t;
	for(i=1;i<=m;++i)
	 {
	 	for(j=0;j<=n;++j)
	 	 {
	 	 	f[i][j]=f[i-1][j];
	 	 	for(int k=0;k<i;++k)
			  if((t=sum[k+1][i]))
			   {
			   	 if(f[k][j]!=INF) f[i][j]=max(f[i][j],f[k][j]+t);
				 if(j-t>=0&&f[k][j-t]!=INF) f[i][j]=max(f[i][j],f[k][j-t]);	 
			   }    
		  }
		for(j=n-1;~j;--j) f[i][j]=max(f[i][j],f[i][j+1]);
	 }
	int ans=0;
	for(i=0;i<=m;++i) ans=max(ans,min(i,f[m][i]));
	printf("%d\n",ans);//求初始时的演出较少的会场最多演多少场
	 
	for(i=m;i>0;--i)
	 {
	 	for(j=0;j<=n;++j)
	 	 {
	 	 	g[i][j]=g[i+1][j];
	 	 	for(int k=m+1;k>i;--k)
	 	 	 if((t=sum[i][k-1]))
	 	 	  {
	 	 	  	if(g[k][j]!=INF) g[i][j]=max(g[i][j],g[k][j]+t);
				if(j-t>=0&&g[k][j-t]!=INF) g[i][j]=max(g[i][j],g[k][j-t]);	 
			  }
		  }
		for(j=n-1;~j;--j) g[i][j]=max(g[i][j],g[i][j+1]);
	 } 
	for(i=1;i<=m;++i)
	 for(j=i;j<=m;++j)
	  if(sum[i][j])
	   {
	   	    int y=n;
	   	    for(int k=0;k<=n;++k)
	   	     {
	   	     	int now=get(i,j,k,y);
	   	     	while(y&&now<=(t=get(i,j,k,y-1))) {now=t; y--;}
	   	     	dp[i][j]=max(now,dp[i][j]);
				}
		} 
	for(int k=1;k<=n;++k)
	 {
	 	ans=0;
	 	for(i=a[k].s;i>0;--i)
	 	 for(j=a[k].t;j<=m;++j)
	 	  ans=max(ans,dp[i][j]);
	 	printf("%d\n",ans);
	 }
	return 0;
}
 </strong></span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值