1 条题解

  • 0
    @ 2025-8-24 21:32:29

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar FlashHu
    **

    搬运于2025-08-24 21:32:29,当前版本为作者最后更新于2018-08-13 17:39:26,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    DP题怕是都要大大的脑洞。。。。。。

    首先,时间那么大没用,直接离散化。

    第一问还好。根据题意容易发现,当一堆活动的时间有大量重叠的时候,更好的办法是把它们全部安排到一边去。那么我们转移的时候也肯定是要一块一块地转移啦。

    totl,rtot_{l,r}为完全被包含在lrl-r时间内活动总数,直接O(n3)O(n^3)暴力求就好了。

    prei,jpre_{i,j}为时间1i1-i内一边选jj个时,另一边能选的最大值。枚举一块转移的话,我们的方程应该写成这样:

    $$pre_ {i,j}=\max\limits_{k=1}^i\{pre_ {k,j}+tot_{k,i},pre _{k,j-tot _{k,i}}\} $$

    (显然两种情况都要考虑)

    然后答案就是maxj=1n{min(prem,j,j)}\max\limits_{j=1}^n\{\min(pre_{m,j},j)\}啦(mm为离散化后的时间总长,不会超过2n2n

    这个数组为什么要叫prepre呢?这是个前缀DP值。为了第二问,我们还要做个后缀DP,sufi,jsuf_{i,j}表示时间imi-m内一边选jj个时,另一边能选的最大值,跟prepre几乎一样的转移,也是O(n3)O(n^3)的。

    对于第二问,我们显然可以肯定sitis_i-t_i之内的活动都被一边选走了。至于sis_i之前和tit_i以后选了多少,我们也只好枚举。设fl,rf_{l,r}为一边强制选lrl-r之间所有活动时最优的最小值,假定这一边在前面选了xx个,在后面选了yy个,另一边最多能选多少也就知道了,有方程

    $$f_{l,r}=\max\limits_{x=1}^m\max\limits_{y=1}^m\{\min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y})\} $$

    然后第ii个的答案就是fsi,tif_{s_i,t_i}么?注意千万别掉入这个误区!prepresufsuf只是保证了局部最优,而没有保证全局最优。要说人话的话,就是可能有一个活动跨过了sis_i,然而fsi,tif_{s_i,t_i}并没有统计到它,只有扩大强制选的区间使得能够包含它,才能统计到最优解。于是需要枚举强制选区间了,$ans_i=\max\limits_{l=1}^{s_i}\max\limits_{r=t_i}^m\{f_{l,r}\}$

    这样的话,整个ff都必须要算出来,上面的枚举算法就变成O(n4)O(n^4)了,跑不动。

    点开标签发现有单调队列?!蒟蒻就往单调性上面想了想,于是就有了一个结论:设枚举xx时有一个使答案最优的yy,那么当xx增大时,如果yy也增大那么答案不会更优。观察上面那个式子min(x+totl,r+y,prel,x+sufr,y)\min(x+tot_{l,r}+y,pre_{l,x}+suf_{r,y}),那么因为pre,sufpre,suf都是递减的,所以很显然我们不能让x,yx,y变大而pre,sufpre,suf变小。

    于是,实现的时候,只要把yy从大往小扫了,并不需要什么单调队列来维护它。

    #include<cstdio>
    #include<algorithm>
    #define RG register
    #define R RG int
    #define G c=getchar()
    #define Upd(A,L,R)     {chkmx(A[i][j],A[k][j]+tot[L][R]);	\
    		if(j>=tot[L][R])chkmx(A[i][j],A[k][j-tot[L][R]]);}
    #define Calc(y) min(x+tot[l][r]+y,pre[l][x]+suf[r][y])
    using namespace std;
    const int N=209,M=409,INF=1e9;
    int s[N],t[N],b[M],tot[M][M],pre[M][N],suf[M][N],f[M][M];
    inline int in(){
    	RG char G;
    	while(c<'-')G;
    	R x=c&15;G;
    	while(c>'-')x=x*10+(c&15),G;
    	return x;
    }
    inline int min(R x,R y){return x<y?x:y;}
    inline void chkmx(R&x,R y){if(x<y)x=y;}
    int main(){
    	R n=in(),m=0,i,j,k,l,r,x,y,p0,p1,ans;
    	for(i=1;i<=n;++i){
    		b[++m]=s[i]=in();
    		b[++m]=t[i]=in()+s[i];
    	}
    	sort(b+1,b+m+1);//离散化
    	m=unique(b+1,b+m+1)-b-1;
    	for(i=1;i<=n;++i){
    		s[i]=lower_bound(b+1,b+m+1,s[i])-b;
    		t[i]=lower_bound(b+1,b+m+1,t[i])-b;
    		for(l=1;l<=s[i];++l)//tot暴力求
    			for(r=m;r>=t[i];--r)++tot[l][r];
    	}
    	for(i=1;i<=m;++i)//注意初始化
    		for(j=1;j<=n;++j)pre[i][j]=suf[i][j]=-INF;
    	for(i=1;i<=m;++i)
    		for(j=0;j<=tot[1][i];++j)
    			for(k=1;k<=i;++k)Upd(pre,k,i);
    	for(i=m;i;--i)//转移很相似,搞了个宏定义
    		for(j=0;j<=tot[i][m];++j)
    			for(k=i;k<=m;++k)Upd(suf,i,k);
    	for(l=1;l<=m;++l)
    		for(r=l+1;r<=m;++r)
    			for(y=n,x=0;x<=n;++x){
    				p0=Calc(y);//p0为最优决策,p1为当前决策
    				while(y&&p0<=(p1=Calc(y-1)))p0=p1,--y;
    				chkmx(f[l][r],Calc(y));
    			}
    	ans=0;
    	for(j=1;j<=n;++j)chkmx(ans,min(pre[m][j],j));
    	printf("%d\n",ans);
    	for(i=1;i<=n;++i){
    		ans=0;
    		for(l=1;l<=s[i];++l)
    			for(r=m;r>=t[i];--r)chkmx(ans,f[l][r]);
    		printf("%d\n",ans);
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    939
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者