传送门
非常神的一道dp题,感觉在考场上想出来是十分困难的。
首先对于第一问,先将区间离散,那么所有的时间就都变成 O(n) 级别的了。可以预处理出 sum[i][j] 表示时间i~j内有多少个活动。设 f[i][j] 表示到第i时间为止,第一个场地已经举办了j场活动时第二个场地最多举办多少场活动。
求f的过程可以用一个 O(n3) 的dp,转移方程为 f[i][j]=max{f[i−1][j],f[k][j]+sum[k+1][i],f[k][j−sum[k+1][i]]}
分别表示不举行活动,在二号场地举行活动和在一号场地举行活动。
那么第一问的答案就是 max{min(i,f[time][i])}
对于第二问,再用同样的方法求 g[i][j] 表示倒序时间到i时,第一个场地举办了j个活动时第二个场地最多举办多少个活动。
设 dp[i][j] 表示i~j这一段所有的活动同时选到某一个场地的最大答案。
显然如果我们必须选的是区间(l,r)
那答案就是 max{dp[i][j](i<=x,j>=y)}
dp[i][j] 怎么求?
dp[i][j]=max{min(min(x+y,f[i−1][x]+g[j+1][y])+sum[i][j],max(x+y,f[i−1][x]+g[j+1][y]))} 。
这样复杂度是 O(n4) .
考虑如何优化。
我们假设随着x上升时,y也增加,那么显然 f[i−1][x] 和 g[j+1][y] 都是不增的。从要使 x+y 和 f[i−1][x]+g[j+1][y] ,尽量平均的角度考虑,这样的情况是不可能出现的。
所以我们得出了一个结论:随着x上升,在最优处的y值是单调不增的。
时间复杂度可以优化到 O(n3) .
#include
#include
#include
#include
using namespace std;
#define N 205
int n,s,t,xx,lsh,ans,inf;
int X[N*2],p[N*2],loc[N*2],sum[N*2][N*2],f[N*2][N],g[N*2][N],dp[N*2][N*2];
struct hp{int l,r;}show[N];
inline int cmp(int a,int b)
{
return X[a]inline int get(int i,int j,int x,int y)
{
if (f[i-1][x]==inf||g[j+1][y]==inf) return inf;
return min(min(x+y,f[i-1][x]+g[j+1][y])+sum[i][j],max(x+y,f[i-1][x]+g[j+1][y]));
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
scanf("%d%d",&s,&t);
X[++xx]=s,X[++xx]=s+t-1;
}
for (int i=1;i<=xx;++i) p[i]=i;
sort(p+1,p+xx+1,cmp);X[0]=-1;
for (int i=1;i<=xx;++i)
if (X[p[i]]!=X[p[i-1]]) loc[p[i]]=++lsh;
else loc[p[i]]=lsh;
xx=0;
for (int i=1;i<=n;++i)
show[i].l=loc[++xx],show[i].r=loc[++xx];
for (int i=1;i<=lsh;++i)
for (int j=i;j<=lsh;++j)
for (int k=1;k<=n;++k)
if (show[k].l>=i&&show[k].r<=j)
sum[i][j]++;
memset(f,128,sizeof(f));
inf=f[0][0];
f[0][0]=0;
for (int i=1;i<=lsh;++i)
for (int j=0;j<=sum[1][i];++j)
{
f[i][j]=f[i-1][j];
for (int k=0;k1][i]);
if (j-sum[k+1][i]>=0) f[i][j]=max(f[i][j],f[k][j-sum[k+1][i]]);
}
}
ans=0;
for (int i=0;i<=n;++i)
ans=max(ans,min(i,f[lsh][i]));
printf("%d\n",ans);
memset(g,128,sizeof(g));
g[lsh+1][0]=0;
for (int i=lsh;i>=1;--i)
for (int j=0;j<=n;++j)
{
g[i][j]=g[i+1][j];
for (int k=lsh+1;k>i;--k)
{
g[i][j]=max(g[i][j],g[k][j]+sum[i][k-1]);
if (j-sum[i][k-1]>=0) g[i][j]=max(g[i][j],g[k][j-sum[i][k-1]]);
}
}
for (int i=1;i<=lsh;++i)
for (int j=i;j<=lsh;++j)
{
int y=n;
for (int x=0;x<=n;++x)
{
int now=get(i,j,x,y);
while (y&&now<=(t=get(i,j,x,y-1))){now=t; y--;}
dp[i][j]=max(now,dp[i][j]);
}
}
for (int k=1;k<=n;++k)
{
ans=0;
for (int i=1;i<=show[k].l;++i)
for (int j=show[k].r;j<=lsh;++j)
ans=max(ans,dp[i][j]);
printf("%d\n",ans);
}
}