【题解】P1020 导弹拦截

题面

题目传送门

前言

本来开开心心敲个 O(n2) 走人的……

我嘞个超绝 200 分啊!

正文

首先嗷,这个题是可以 O(n2) 过滴!

转化一下题意:第一问即求最长不上升子序列的长度,第二问即求最少能被划分成多少个不上升子序列

第一问

DP 是非常好想的,记 dp1i 为截止到 iai 必选的第一问的答案

转移方程:dp1i=max{dp1j+1},ajai

答案的计算也是显然的……形如:ans=maxi=1n{dp1i}

但是捏,我们要追求一下 200 分,需要给出一种优于 O(n2) 的算法

这个东西长得就很像决策单调性优化 DP 的套路

如何证明该 DP 转移有决策单调性?

fi 表示对于所有长度i 的单调不升子序列,它的最后一项的大小的最大值。特别地,若不存在则 fi=0

显然有 fi[1,n] 上单调不升

不严格的证明
考虑使用反证法。假设 u<v,满足 fu<fv
考虑长度为 v 的单调不升子序列,根据定义它以 fv 结尾
而我们可以从该序列中构造出一个长度为 u 的单调不升子序列,它的结尾同样是 fv,那么由于 fv>fu,与 fu 最大相矛盾,故假设不成立

嗯嗯,对于 fi 的单调性我们有了不严格的证明捏!

现在考虑以 i 结尾的单调不升子序列的长度的最大值 dp1i

由于我们需要计算所有满足 hj>hi 中,dp1j 的最大值(我们称 ji 的决策点,显然有 j 是关于 i 的函数)

考察这个 dp1j 的最大值,不妨设 x=dp1j,那么如果 ai>fx,由于 fxaj 就有 ai>aj,与 aiaj 矛盾,故假设不成立

因此总有 aifx

以上我们证明了 DP 具有的决策单调性,采用二分法即可(当然 upper_boundlower_bound 好用捏!)

时间复杂度 O(nlogn)

第二问

前置知识:Dilworth 定理

Dilworth 定理
最长反链 = 最小链覆盖

然后第二问就进一步转化为求最长上升子序列的长度

同上即可!

代码

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e6+10;
int n=1,a[maxn];
int dp1[maxn],dp2[maxn];
int len1,len2;
signed main(){
	while(cin>>a[n]){
		n++;
	}
	n--;
	len1=1;
	len2=1;
	dp1[1]=a[1];
	dp2[1]=a[1];
	for(int i=2;i<=n;i++){
		if(a[i]<=dp1[len1]){
			dp1[++len1]=a[i];
		}else{
			int p=upper_bound(dp1+1,dp1+len1+1,a[i],greater<int>())-dp1;
			dp1[p]=a[i];
		}
		if(a[i]>dp2[len2]){
			dp2[++len2]=a[i];
		}else{
			int p=lower_bound(dp2+1,dp2+len2+1,a[i])-dp2;
			dp2[p]=a[i];
		}
	}
	cout<<len1<<endl<<len2<<endl;
	return 0;
}

后记

也许,云落真的很……

完结撒花!

posted @   sunxuhetai  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示