【题解】
这道题不是按"第i个活动在哪个嘉年华举办"来进行决策的,而是利用题目"嘉年华A与B的活动时间无交叉"的性质,在离散化时间的基础上表示状态:
pre[i][j]表示:时间[1,i]中有j个活动在嘉年华A举办时,嘉年华B举办的最大活动数状态转移:先预处理得num[i][j]:离散化后时间满足i<=s<s+t<=j的活动数量
pre[i][j]=max{ pre[k][j]+num[k][i] , pre[k][j-num[k][i]] } ,1<=k<i (分为前i时间内举办完的最后一项活动在B中还是A中两种情况)
第一问的答案就是: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<k<=T
预处理出所有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<y2,且函数值相等,当y==y2时,若y不左移,就没有机会达到当前x对应的y3。而x加1后,峰值变化,不一定能取到最优值了
所以,用<延迟更新是错误的
【代码】
#include<stdio.h>
#include<stdlib.h>
#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(a<b) return a;
return b;
}
int max(int a,int b)
{
if(a>b) 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(i<j)
{
while(a[i]<mid) i++;
while(a[j]>mid) j--;
if(i<=j)
{
jh(&a[i],&a[j]);
jh(&c[i],&c[j]);
i++;
j--;
}
}
if(j>low) kp(low,j);
if(i<high) kp(i,high);
}
int main()
{
int n,T=0,i,j,k,x,y,Ans=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&s[i],&t[i]);
t[i]+=s[i];
a[2*i-1]=s[i];
a[2*i]=t[i];
}
for(i=1;i<=2*n;i++)
c[i]=i;
kp(1,2*n);
for(i=1;i<=2*n;i++)
{
if(i==1||a[i]!=a[i-1]) T++;//离散化后时间从1开始
if(c[i]%2==1) s[c[i]/2+1]=T;
else t[c[i]/2]=T;
}
for(i=1;i<=n;i++)
for(j=1;j<=s[i];j++)
for(k=t[i];k<=T;k++)
num[j][k]++;
for(i=1;i<=T;i++)
for(j=1;j<=n;j++)
pre[i][j]=suc[i][j]=-INF;
for(i=1;i<=T;i++)
for(j=0;j<=n;j++)
for(k=1;k<i;k++)
{
pre[i][j]=max(pre[i][j],pre[k][j]+num[k][i]);
if(j>=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;i<T;i++)
for(j=i+1;j<=T;j++)
{
y=num[j][T];
for(x=0;x<=num[1][i];x++)
{
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--;
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;
}