洛谷 P1973 [NOI2011]Noi嘉年华【离散化】【dp】


题目:

传送门


题意:

给出 n n n个活动的开始时间和持续时长,问在不限制必须举办哪一场和在有第 i ( i ∈ ( 1 − n ) ) i(i\in(1-n)) i(i(1n))限制下,两个会场中举办较少的那一个的活动数最大


分析:

因为时间数值非常大,所以我们果断先离散化一遍。
为了方便表示,我们设 w i , j w_{i,j} wi,j代表完全在 i — j i—j ij这一段时间内举行的活动总数, t t t表示最后一个活动在什么时候结束
f i , j f_{i,j} fi,j表示在第 1 — i 1—i 1i的时间里,一个会场举办 j j j场活动,另一个会场最多举行多少
那么我们进行分类讨论得出方程:
1. 1. 1.如果在 i i i前已经举办了 j j j场,那么 w k , i ( k ∈ ( 1 — i − 1 ) ) w_{k,i}(k\in(1—i-1)) wk,i(k(1i1))只能在另一个会场举行,也就说
f i , j = f k , j + w k , i f_{i,j}=f_{k,j}+w_{k,i} fi,j=fk,j+wk,i
2. 2. 2.如果在 i i i前没有举办完 j j j场,那么 w k , i w_{k,i} wk,i就只能在这一个会场举行,以保证这一个会场在 i i i时能举办 j j j
f i , j = f k , j − w k , j f_{i,j}=f_{k,j-w_{k,j}} fi,j=fk,jwk,j
初始化则是 f i , 0 = w 1 , i f_{i,0}=w_{1,i} fi,0=w1,i
g i , j g_{i,j} gi,j表示在 j — t j—t jt的时间里,一个会场举办 j j j场活动,另一个会场最多举办多少
因为跟上面的 f i , j f_{i,j} fi,j思路基本一致,所以这里就直接写出方程了
g i , j = { g k , j + w i , k g k , j − w i , k w i , t                        ( j = 0 ) g_{i,j}=\left\{\begin{matrix} g_{k,j}+w_{i,k}\\ g_{k,j-w_{i,k}}\\ w_{i,t}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (j=0) \end{matrix}\right. gi,j=gk,j+wi,kgk,jwi,kwi,t                      (j=0)
到了这一步,我们开始思考如何表达出答案了
第一问其实就相当于对后面有限制时的答案取 m a x max max,这样一来我们的重心就转移到如何做到必须选某个活动和如何表示出 a n s i ans_i ansi
l l l表示当前一个会场举办的活动都在 l l l以后, r r r表示当前该会场举办的活动都在 r r r以前
对于第一个如何,因为我们已经知道了每个活动的开始和结束时间,所以直接将 l = 开 始 时 间 l=开始时间 l= r = 结 束 时 间 r=结束时间 r=。因为 l l l是逐渐接近 1 1 1 r r r是逐渐接近 t t t的,所以这样我们就做到了保证一个会场举办该活动
对于第二个如何,我们设 w l wl wl表示在 l l l以前,该会场举办了多少活动, w r wr wr表示在 r r r以后,该会场举办了多少活动
那么就可以得到方程:
a n s i = m a x { a n s i , m i n { w l + w r + w l , r , f l , w l + g r , w r } } ans_i=max\{ans_i,min\{wl+wr+w_{l,r},f_{l,wl}+g_{r,wr}\}\} ansi=max{ansi,min{wl+wr+wl,r,fl,wl+gr,wr}}


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long 
#define LZX IMS
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}   
int a[205],b[205],begin[205],end[205],p[405],q[405],t=0;
int w[405][405],f[405][405],g[405][405],ans[205];
int main()
{
	int n=read();
	for(int i=1;i<=n;i++) {a[i]=read();b[i]=read()+a[i];p[++t]=a[i];p[++t]=b[i];}
	sort(p+1,p+1+t);
	q[1]=1;
	for(int i=2;i<=t;i++) if(p[i]==p[i-1]) q[i]=q[i-1]; else q[i]=q[i-1]+1;
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=t;j++)
	  {
	  	if(p[j]==a[i]) begin[i]=q[j];
	  	if(p[j]==b[i]) end[i]=q[j];
	  }
	t=q[t];
	for(int i=1;i<=t;i++)
	  for(int j=i+1;j<=t;j++)
	    for(int k=1;k<=n;k++)
	      if(begin[k]>=i&&end[k]<=j) w[i][j]++;
	memset(f,-0x3f,sizeof(f));memset(g,-0x3f,sizeof(g));
	for(int i=1;i<=t;i++)
	  for(int j=0;j<=min(n/2,w[1][i]);j++)
	  {
	  	if(!j) f[i][0]=w[1][i];
	  	for(int k=1;k<i;k++) f[i][j]=max(f[i][j],max(f[k][j]+w[k][i],f[k][j-w[k][i]]));
	  }
	for(int i=t;i;i--)
	  for(int j=0;j<=min(n/2,w[i][t]);j++)
	  {
	  	if(!j) g[i][0]=w[i][t];
	  	for(int k=i+1;k<=t;k++) g[i][j]=max(g[i][j],max(g[k][j]+w[i][k],g[k][j-w[i][k]]));
	  }
	for(int i=1;i<=n;i++)
	{
	  for(int j=begin[i];j;j--)
	    for(int k=end[i];k<=t;k++)
	    {
	      for(int l=0;l<=w[1][j];l++)
	      {
	        for(int r=0;r<=w[k][t];r++)
	        {
			  ans[i]=max(ans[i],min(l+r+w[j][k],f[j][l]+g[k][r]));
              if(g[k][r]<r) break;       	  
			}		
			if(f[j][l]<l) break;
		  }
		  if(w[1][j]+w[k][t]<ans[i]) break;	
		}
	  ans[0]=max(ans[0],ans[i]);
	}
	for(int i=0;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值