[NOI2011]Noi嘉年华

题解:
我们设计状态方程如下:
num[i][j]表示从时间i到j中有多少个
pre[i][j]表示时间1~i中,A选了j个时的B能选的数量的最大值.
nex[i][j]表示时间i~cnt中,A选了j个时的B能选的数量的最大值.
mus[i][j]表示从时间i到j的保证选时,A和B选的数量中的较小值的最大值.
①对于num数组直接可以暴力求,没问题.
②对于pre和nex数组在这里就只讲pre的转移,nex的转移类似.
pre[i][j]=max(pre[i-1][j],pre[k][j]+num[k][i],pre[k][j-num[k][i]]);(1<=k<i)
就是分成不管i时间,或者从时间k到i的都由B选,或者从时间k到i的都由A选三种情况
③对于mus数组,我们得到转移方程mus[i][j]=max(min(x+y,pre[i][x]+num[i][j]+nex[j][y])),x表示时间1~i中A选x个,y表示时间j~cnt中A选y个,中间num[i][j]个由B选.这样暴力转移的话是O(n^4)的,时间上是不允许的,那么我们观察转移方程,当x增大时,y只有单调递减答案才会更优,因为如果x增大且y也增大的话,表示A选的越来越多,B选的越来越少,显然答案不会更优的,所以我们维护一个y的单调递减的指针来转移就可以了,时间O(n^3).
④对于第一问的答案就是max(min(pre[i][j],j)),枚举时间i和A选的个数j来得到最优解.
然后对于第二问我们对于一个活动l~r,答案为max(mus[i][j]),1<=i<=l,r<=j<=cnt.

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define MAXN 10100
#define RG register
#define LL long long int
using namespace std;
const int INF=1e9;
struct node{
  int l,r;int id;
}t[1010];
int n;
int num[1010][1010];//从时间i到j中有多少个
int pre[1010][1010];//时间1~i中,A选了j个时的B能选的数量的最大值.
int nex[1010][1010];//时间i~n中,A选了j个时的B能选的数量的最大值.
int mus[1010][1010];//从时间i到j的保证选时,A和B选的数量中的较小值的最大值.
int st[MAXN],cnt;
int ans[MAXN];
int main()
{
  freopen("1.in","r",stdin);
  scanf("%d",&n);
   for(int i=1;i<=n;i++){
    scanf("%d%d",&t[i].l,&t[i].r);t[i].r+=t[i].l;t[i].id=i;
    st[++cnt]=t[i].l;st[++cnt]=t[i].r;
  }
  sort(st+1,st+cnt+1);cnt=unique(st+1,st+cnt+1)-st-1;
  for(int i=1;i<=n;i++){
    t[i].l=lower_bound(st+1,st+cnt+1,t[i].l)-st;
    t[i].r=lower_bound(st+1,st+cnt+1,t[i].r)-st;
    }
  for(int i=1;i<=cnt;i++)
    for(int j=1;j<=t[i].l;j++)
      for(int k=t[i].r;k<=cnt;k++)
	num[j][k]++;
  for(int i=1;i<=cnt;i++)
    for(int j=1;j<=n;j++)
      pre[i][j]=nex[i][j]=-INF;
  pre[0][0]=nex[cnt+1][0]=0;
  for(int i=1;i<=cnt;i++){
    for(int j=0;j<=num[1][i];j++)
      pre[i][j]=max(pre[i-1][j],pre[i][j]);
    for(int j=0;j<=num[1][i];j++){
      for(int k=1;k<i;k++){
	pre[i][j]=max(pre[k][j]+num[k][i],pre[i][j]);
	if(j>=num[k][i]) pre[i][j]=max(pre[k][j-num[k][i]],pre[i][j]);
      }
    }
  }
  for(int i=cnt;i>=1;i--){
    for(int j=0;j<=num[i][cnt];j++)
      nex[i][j]=max(nex[i+1][j],nex[i][j]);
    for(int j=0;j<=num[i][cnt];j++){
      for(int k=cnt;k>i;k--){
	nex[i][j]=max(nex[k][j]+num[i][k],nex[i][j]);
	if(j>=num[i][k]) nex[i][j]=max(nex[k][j-num[i][k]],nex[i][j]);
      }
    }
  }
  for(int i=1;i<=cnt;i++)
    for(int j=i+1;j<=cnt;j++)
      {
	for(int x=0,y=n;x<=n;x++){
	  while(y){
	    int val1=min(x+y,pre[i][x]+num[i][j]+nex[j][y]);
	    int val2=min(x+y-1,pre[i][x]+num[i][j]+nex[j][y-1]);
	    if(val2>=val1) y--;else break;
	  }
	  mus[i][j]=max(mus[i][j],min(x+y,pre[i][x]+num[i][j]+nex[j][y]));
	}
      }
  for(int i=1;i<=cnt;i++)
    for(int j=0;j<=num[1][i];j++)
      ans[0]=max(ans[0],min(pre[i][j],j));
  for(int i=1;i<=n;i++)
      for(int j=t[i].l;j>=1;j--)
	for(int k=t[i].r;k<=cnt;k++)
	  ans[i]=max(ans[i],mus[j][k]);
  for(int i=0;i<=n;i++) printf("%d
",ans[i]);
  return 0;
}
原文地址:https://www.cnblogs.com/Landlord-greatly/p/8144349.html