【洛谷1973】[NOI2011] NOI 嘉年华(DP)

点此看题面

大致题意:\(n\)个活动,每个活动可以表示为一个区间。你要把活动分给两个嘉年华(可以不分完),使得不会同时在两个嘉年华举行活动(同一嘉年华中没有限制),求两个嘉年华活动数中较小值的最大值。然后对于每个\(i\),求出在强制选择第\(i\)个活动时的答案。

前言

个人感觉这道题应该还是比较简单的,反正我基本上都能自己推出来。

自己写出一道\(DP\)题还是挺有成就感的吧!

第一问

这么小的数据范围,第一问应该是非常简单,也非常容易想到的。

\(f_{i,j}\)为在\([1,i]\)这段时间中,给第一个嘉年华\(j\)个活动时,第二个嘉年华活动数的最大值。

考虑如何转移,其实就是枚举\(k\)作为关键点,并分别讨论把\([k+1,i]\)这段时间中的活动放入哪个嘉年华,得到:(\(s_{i,j}\)为完全在\([i,j]\)这段时间中的活动个数)

\[f_{i,j}=\max_{k=1}^{i-1}\{f_{k,j}+s_{k+1,i},f_{k,j-s_{k+1,i}}\} \]

则第一问的答案显然就是:

\[\max_{j=0}^n\min\{f_{T,j},j\} \]

然后为了方便处理第二问,我们类似于\(f_{i,j}\)去定义一个\(g_{i,j}\)表示在\([i,T]\)这段时间中,给第一个嘉年华\(j\)个活动时,第二个嘉年华活动数的最大值。那么转移的过程也类似于\(f_{i,j}\)

第二问

容易想到一个最暴力的做法,就是枚举出一个区间\([i,j]\)包含当前活动区间\([a_{now},b_{now}]\),将这段时间作为完整一段放入一个嘉年华中,得出答案式:

\[\max_{i=1}^{a_{now}}\max_{j=b_{now}}^T\max_{x=1}^n\max_{y=1}^n\min\{x+y+s_{i,j},f_{i-1,x}+g_{j+1,y}\} \]

这个式子中有\(4\)重循环,如果还要再枚举\(now\),就是\(5\)重循环。但实际上我们可以事先预处理好强制选择每一个\([i,j]\)时的答案\(w_{i,j}\),时间复杂度就优化为了\(O(n^4)\)

\(n\le200\)显然就是在刻意卡\(O(n^4)\),还需要进一步优化。

考虑随着\(x\)的增大,\(y\)的最优决策点一定单调递减,因此只要采用双指针,就可以把时间复杂度优化至\(O(n^3)\)了。

最终答案为:

\[\max_{i=1}^{a_{now}}\max_{j=b_{now}}^T w_{i,j} \]

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,a[N+5],b[N+5],dc,dv[2*N+5],s[2*N+5][2*N+5],f[2*N+5][N+5],g[2*N+5][N+5],w[2*N+5][2*N+5];
int main()
{
	RI i,j,k;for(scanf("%d",&n),i=1;i<=n;++i)
		scanf("%d%d",a+i,b+i),dv[n+i]=(b[i]+=(dv[i]=a[i])-1);//读入区间
	for(sort(dv+1,dv+2*n+1),dc=unique(dv+1,dv+2*n+1)-dv-1,i=1;i<=n;++i)
	{
		a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv,b[i]=lower_bound(dv+1,dv+dc+1,b[i])-dv;//离散化
		for(j=a[i];j;--j) for(k=b[i];k<=dc;++k) ++s[j][k];//求出每段区间内活动数
	}
	memset(f,-63,sizeof(f)),memset(g,-63,sizeof(g)),f[0][0]=g[dc+1][0]=0;//初始化
	for(i=1;i<=dc;++i) for(j=s[1][i];~j;--j) for(f[i][j]=f[i][j+1],k=0;k^i;++k)//求f数组
		Gmax(f[i][j],f[k][j]+s[k+1][i]),j>=s[k+1][i]&&Gmax(f[i][j],f[k][j-s[k+1][i]]);
	for(i=dc;i>=1;--i) for(j=s[i][dc];~j;--j) for(g[i][j]=g[i][j+1],k=dc+1;k^i;--k)//求g数组
		Gmax(g[i][j],g[k][j]+s[i][k-1]),j>=s[i][k-1]&&Gmax(g[i][j],g[k][j-s[i][k-1]]);
	RI t=0;for(i=0;i<=n;++i) Gmax(t,min(f[dc][i],i));printf("%d\n",t);//求解第一问
	#define GV(y) min(x+(y)+s[i][j],f[i-1][x]+g[j+1][y])
	RI x,y;for(i=1;i<=dc;++i) for(j=i;j<=dc;++j)//预处理强制选择区间[i,j]的答案
		for(x=0,y=n;x<=n;++x) {W(y&&GV(y)<=GV(y-1)) --y;Gmax(w[i][j],GV(y));}//注意最优决策点的单调性
	for(i=1;i<=n;++i,printf("%d\n",t))//枚举每一个活动
		for(t=0,j=1;j<=a[i];++j) for(k=b[i];k<=dc;++k) Gmax(t,w[j][k]);return 0;//求出强制选择该活动时的答案
}
posted @ 2020-06-16 19:45  TheLostWeak  阅读(15)  评论(0编辑  收藏