题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2436
思路
这个题做起来很复杂很繁琐。。。以下思路整理自http://blog.csdn.net/whjpji/article/details/7547159
首先将所有的区间离散化,这一步很好实现。
然后就是求三个数组
num[i][j],pre[i][j],suf[i][j]
num[i][j]=[i,j]
区间内的线段个数。
pre[i][j]=[0,i]
区间内给B
j
个线段,A得到最多线段个数。
容易推出
然后就是 pre[i][j]和suf[i][j] 了,这两个其实基本上一样,下面只讲 pre[i][j] 的求法。
(注:区间 [0,i] 中给B放入 j 个线段,可以枚举
然后就要求一个
g[i][j]
,
g[i][j]=[i,j]
区间内的所有线段必须选,使得A和B中保含线段少的那个集合里的线段个数最多多少。
令
x 是
在
x
固定的情况下,
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 410
#define INF 0x3f3f3f3f
using namespace std;
struct Segment
{
int L,R;
}seg[MAXN*2];
int stack[MAXN*2],top=0,n;
int pre[MAXN][MAXN],suf[MAXN][MAXN],g[MAXN][MAXN];
int num[MAXN][MAXN];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&seg[i].L,&seg[i].R);
seg[i].R+=seg[i].L; //!!!!
stack[top++]=seg[i].L;
stack[top++]=seg[i].R;
}
sort(stack,stack+top);
top=unique(stack,stack+top)-stack;
for(int i=1;i<=n;i++)
{
seg[i].L=lower_bound(stack,stack+top,seg[i].L)-stack;
seg[i].R=lower_bound(stack,stack+top,seg[i].R)-stack;
}
for(int i=0;i<top;i++) //预处理出num[i][i]~num[i][top]
{
for(int j=1;j<=n;j++) //!!!!!
if(seg[j].L>=i)
num[i][seg[j].R]++;
for(int j=i+1;j<top;j++) //!!!!!
num[i][j]+=num[i][j-1];
}
memset(pre,~INF,sizeof(pre));
memset(suf,~INF,sizeof(suf));
pre[0][0]=0;
suf[top-1][0]=0;
for(int i=0;i<top;i++)
{
for(int j=0;j<=n;j++)
if(pre[i][j]>~INF)
pre[i][pre[i][j]]=max(pre[i][pre[i][j]],j);
for(int j=n-1;j>=0;j--)
pre[i][j]=max(pre[i][j],pre[i][j+1]);
for(int j=0;j<=n;j++)
for(int k=i+1;k<top;k++) //向后递推,[0,i]里给A放入j个区间,那么用[0,k]里在B中放入pre[i][j]个区间,在A中放入j+num[i][k]个区间的方案更新答案
if(pre[i][j]>~INF)
pre[k][pre[i][j]]=max(pre[k][pre[i][j]],j+num[i][k]);
}
for(int i=top-1;i>=0;i--)
{
for(int j=0;j<=n;j++)
if(suf[i][j]>~INF)
suf[i][suf[i][j]]=max(suf[i][suf[i][j]],j);
for(int j=n-1;j>=0;j--)
suf[i][j]=max(suf[i][j],suf[i][j+1]);
for(int j=0;j<=n;j++)
for(int k=i-1;k>=0;k--) //向前递推,[i,INF)里给A放入j个区间,那么用[k,INF)里在B中放入suf[i][j]个区间,在A中放入j+num[k][i]个区间的方案来更新答案
if(suf[i][j]>~INF)
suf[k][suf[i][j]]=max(suf[k][suf[i][j]],j+num[k][i]);
}
for(int i=0;i<top;i++) //求g[i][j]=必须使用[i,j]内的区间
for(int j=i;j<top;j++)
{
g[i][j]=~INF;
for(int x=0,y=n;x<=n;x++)
{
while(y>=0&&x+y>num[i][j]+pre[i][x]+suf[j][y]) y--; //x=[0,i]中B选择的区间个数,y=[j,INF)中B选择的区间个数
if(y>=0) g[i][j]=max(g[i][j],x+y);
}
}
int ans=0;
for(int i=0;i<=n;i++)
ans=max(ans,min(i,suf[0][i]));
printf("%d\n",ans);
for(int i=1;i<=n;i++) //!!!!
{
ans=0;
for(int j=0;j<=seg[i].L;j++)
for(int k=seg[i].R;k<top;k++)
ans=max(ans,g[j][k]);
printf("%d\n",ans);
}
return 0;
}