[NOI2011]Noi嘉年华(动态规划及单调性优化)

【题解】

这道题不是按"第i个活动在哪个嘉年华举办"来进行决策的,而是利用题目"嘉年华A与B的活动时间无交叉"的性质,在离散化时间的基础上表示状态:

pre[i][j]表示:时间[1,i]中有j个活动在嘉年华A举办时,嘉年华B举办的最大活动数 

状态转移:先预处理得num[i][j]:离散化后时间满足i<=s           pre[i][j]=max{ pre[k][j]+num[k][i] , pre[k][j-num[k][i]] } ,1<=k
第一问的答案就是:max{ min(i,pre[T][i]) },T为离散化后的最大时间 

对于第二问,假设必须举办的活动在嘉年华A,且它的时间段包含于嘉年华A中的一段连续时间[i,j](可能与A中别的活动时间重叠),最大答案用ans[i][j]表示 
只需考虑 时间[1,i]与[j,T]的活动举办方式即可,而时间[1,i]中的所有情况,已经用pre[i][j]预处理过了 
那么同理,用suc[i][j]表示:时间[i,T]中有j个活动在嘉年华A举办时,嘉年华B举办的最大活动数 
则 suc[i][j]=max{ suc[k][j]+num[i][k] , suc[k][j-num[i][k]] } ,i 预处理出所有ans[i][j]即可,ans[i][j]=max{ min( x+y+num[i][j] , pre[i][x]+suc[j][y] ) },需要枚举时间[1,i]与[j,T]内,在A中举办的活动数x,y

这样复杂度为O(n^4),优化:利用单调性 
对于确定的i,j,x越大,使min( x+y+num[i][j] , pre[i][x]+suc[j][y] )取最大值的y越小 
证明:反证法证明x增大时,使min最大的y不增,注意x增大前,这个y对于x是最优的,然后分情况:x增大前 左>=右 和 左<右 分别证明 
因此对y的枚举可省略 

询问时,输出 max{ ans[i][j] },[i,j]包含必选活动的时间 

注意:利用y的单峰性求ans[i][j]的这条语句:
while(y>0&&min(x+y+num[i][j],pre[i][x]+suc[j][y])<=min(x+y-1+num[i][j],pre[i][x]+suc[j][y-1])) y--;
其中"<="不能换成"<",理由如图【坑爹啊!!!】
    _(y3)_
   /           \
_/             \____(y1)___(y2)
y1

所以,用<延迟更新是错误的 


【代码】

#include
#include
#define INF 1000000
int num[405][405]={0},ans[405][405]={0},pre[405][205]={0},suc[405][205]={0},s[205]={0},t[205]={0},a[405]={0},c[405]={0};
int min(int a,int b)
{
	if(ab) return a;
	return b;
}
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
void kp(int low,int high)//快排 
{
	int i=low,j=high,mid=a[(i+j)/2];
	while(imid) j--;
		if(i<=j)
		{
			jh(&a[i],&a[j]);
			jh(&c[i],&c[j]);
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i=num[k][i]) pre[i][j]=max(pre[i][j],pre[k][j-num[k][i]]);
			}
	for(i=T;i>=1;i--)
		for(j=0;j<=n;j++)
			for(k=i+1;k<=T;k++)
			{
				suc[i][j]=max(suc[i][j],suc[k][j]+num[i][k]);
				if(j>=num[i][k])suc[i][j]=max(suc[i][j],suc[k][j-num[i][k]]);
			}
	for(i=0;i<=n;i++)
		Ans=max(Ans,min(i,pre[T][i]));
	printf("%d\n",Ans);
	for(i=1;i0&&min(x+y+num[i][j],pre[i][x]+suc[j][y])<=min(x+y-1+num[i][j],pre[i][x]+suc[j][y-1])) y--;
				ans[i][j]=max(ans[i][j],min(x+y+num[i][j],pre[i][x]+suc[j][y]));
			}
		}
	for(i=1;i<=n;i++)
	{
		Ans=0;
		for(j=1;j<=s[i];j++)
			for(k=t[i];k<=T;k++)
				Ans=max(Ans,ans[j][k]);
		printf("%d\n",Ans);
	}
	return 0;
}


你可能感兴趣的:(动态规划)