洛谷P1973 [NOI2011]Noi嘉年华(动态规划,决策单调性)

洛谷题目传送门

DP题怕是都要大大的脑洞。。。。。。

首先,时间那么大没用,直接离散化。

第一问还好。根据题意容易发现,当一堆活动的时间有大量重叠的时候,更好的办法是把它们全部安排到一边去。那么我们转移的时候也肯定是要一块一块地转移啦。

totl,r为完全被包含在lr时间内活动总数,直接O(n3)暴力求就好了。

prei,j为时间1i内一边选j个时,另一边能选的最大值。枚举一块转移的话,我们的方程应该写成这样:

prei,j=maxk=1i{prek,j+totk,i,prek,jtotk,i}

(显然两种情况都要考虑)

然后答案就是maxj=1n{min(prem,j,j)}啦(m为离散化后的时间总长,不会超过2n

这个数组为什么要叫pre呢?这是个前缀DP值。为了第二问,我们还要做个后缀DP,sufi,j表示时间im内一边选j个时,另一边能选的最大值,跟pre几乎一样的转移,也是O(n3)的。

对于第二问,我们显然可以肯定siti之内的活动都被一边选走了。至于si之前和ti以后选了多少,我们也只好枚举。设fl,r为一边强制选lr之间所有活动时最优的最小值,假定这一边在前面选了x个,在后面选了y个,另一边最多能选多少也就知道了,有方程

fl,r=maxx=1mmaxy=1m{min(x+totl,r+y,prel,x+sufr,y)}

然后第i个的答案就是fsi,ti么?注意千万别掉入这个误区!presuf只是保证了局部最优,而没有保证全局最优。要说人话的话,就是可能有一个活动跨过了si,然而fsi,ti并没有统计到它,只有扩大强制选的区间使得能够包含它,才能统计到最优解。于是需要枚举强制选区间了,ansi=maxl=1simaxr=tim{fl,r}

这样的话,整个f都必须要算出来,上面的枚举算法就变成O(n4)了,跑不动。

点开标签发现有单调队列?!蒟蒻就往单调性上面想了想,于是就有了一个结论:设枚举x时有一个使答案最优的y,那么当x增大时,如果y也增大那么答案不会更优。观察上面那个式子min(x+totl,r+y,prel,x+sufr,y),那么因为pre,suf都是递减的,所以很显然我们不能让x,y变大而pre,suf变小。

于是,实现的时候,只要把y从大往小扫了,并不需要什么单调队列来维护它。

#include<cstdio>
#include<algorithm>
#define RG register
#define R RG int
#define G c=getchar()
#define Upd(A,L,R)     {chkmx(A[i][j],A[k][j]+tot[L][R]);	\
		if(j>=tot[L][R])chkmx(A[i][j],A[k][j-tot[L][R]]);}
#define Calc(y) min(x+tot[l][r]+y,pre[l][x]+suf[r][y])
using namespace std;
const int N=209,M=409,INF=1e9;
int s[N],t[N],b[M],tot[M][M],pre[M][N],suf[M][N],f[M][M];
inline int in(){
	RG char G;
	while(c<'-')G;
	R x=c&15;G;
	while(c>'-')x=x*10+(c&15),G;
	return x;
}
inline int min(R x,R y){return x<y?x:y;}
inline void chkmx(R&x,R y){if(x<y)x=y;}
int main(){
	R n=in(),m=0,i,j,k,l,r,x,y,p0,p1,ans;
	for(i=1;i<=n;++i){
		b[++m]=s[i]=in();
		b[++m]=t[i]=in()+s[i];
	}
	sort(b+1,b+m+1);//离散化
	m=unique(b+1,b+m+1)-b-1;
	for(i=1;i<=n;++i){
		s[i]=lower_bound(b+1,b+m+1,s[i])-b;
		t[i]=lower_bound(b+1,b+m+1,t[i])-b;
		for(l=1;l<=s[i];++l)//tot暴力求
			for(r=m;r>=t[i];--r)++tot[l][r];
	}
	for(i=1;i<=m;++i)//注意初始化
		for(j=1;j<=n;++j)pre[i][j]=suf[i][j]=-INF;
	for(i=1;i<=m;++i)
		for(j=0;j<=tot[1][i];++j)
			for(k=1;k<=i;++k)Upd(pre,k,i);
	for(i=m;i;--i)//转移很相似,搞了个宏定义
		for(j=0;j<=tot[i][m];++j)
			for(k=i;k<=m;++k)Upd(suf,i,k);
	for(l=1;l<=m;++l)
		for(r=l+1;r<=m;++r)
			for(y=n,x=0;x<=n;++x){
				p0=Calc(y);//p0为最优决策,p1为当前决策
				while(y&&p0<=(p1=Calc(y-1)))p0=p1,--y;
				chkmx(f[l][r],Calc(y));
			}
	ans=0;
	for(j=1;j<=n;++j)chkmx(ans,min(pre[m][j],j));
	printf("%d\n",ans);
	for(i=1;i<=n;++i){
		ans=0;
		for(l=1;l<=s[i];++l)
			for(r=m;r>=t[i];--r)chkmx(ans,f[l][r]);
		printf("%d\n",ans);
	}
	return 0;
}
posted @   Flash_Hu  阅读(619)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
剑桥
17:14发布
剑桥
17:14发布
5°
西风
7级
空气质量
相对湿度
34%
今天
多云
-3°/5°
周六
-1°/3°
周日
-2°/7°