【洛谷】P1020 导弹拦截/动态规划dp/最长递增子序列/二分优化/树状数组+线段树优化

博客探讨了洛谷P1020导弹拦截问题,涉及动态规划求解最长不递增子序列,利用Dilworth定理简化问题,并讨论了二分查找、桶优化、树状数组和线段树等优化技术来提高算法效率。
摘要由CSDN通过智能技术生成

题目描述

传送门:P1020 导弹拦截
普及/提高-
分析:第一问,很明显求最长不递增子序列,dp模板;第二问,则求不递增子序列的最少个数,根据dilworth定理,不递增子序列的最少个数=最长递增子序列的长度。即两问都是LIS问题。

Dilworth定理

若读者有一定离散基础并有兴趣的话,可直接看百度百科狄尔沃斯定理里的归纳法证明。若无离散和相关数学基础但也有兴趣的话,则推荐B站视频:[Dilworth定理] 洛谷 P1020 导弹拦截
以下给出笔者的一些粗陋想法:
每条不递增子序列都满足两个性质:下标递增且数值不递增。
假设已知有k条最长的不递增序列且除起点和终点外没有交点,那么当加入第j个数时(j=k+1),枚举各序列末尾除重合终点外的数x1,x2…xk,若存在xi(1<=i<=k)不大于xj,那么xj就能加入该序列末尾(符合下标递增且数值不递增的性质),若不存在则说明xj必大于xi(i从1到k的所有值),而xj大于xi使最长递增序列的个数+1,此时只能把xj当作一条新的序列(或者说链)的起点,即每条(除重合起点外)链头的下标和数值都能在上一条中找到比它小的,符合递增子序列性质。
结论:
不递增子序列的最少个数=最长递增子序列的长度
不递减子序列的最少个数=最长递减子序列的长度

朴素dp(O(n^2))

定义dp[i]存储以a[i]结尾的最长不递增子序列的长度。可以想到每放一个a[i]就用j遍历0~i-1,若a[j]>=a[i],说明a[i]可放进a[j]后面,此时dp[i] = dp[j] + 1,因题目取最优解,每次遍历取最大值即可。
状态转移方程:dp[i] = max(dp[i],dp[j]+1)

#include <bits/stdc++.h>
using namespace std;
const int N =  1e5 + 7;
int a[N];
int dp[N];//定义dp[i]=以a[i]结尾的最长不递增序列长度 
int cnt;
int main(){
   
	while (cin>>a[cnt++]);
	for (int i=0;i<cnt-1;i++) dp[i] = 1;
	for (int i=0;i<cnt-1;i++){
   
		for (int j=0;j<i;j++){
   
			if (a[i]<=a[j]) dp[i] = max(dp[i],dp[j]+1);
			//若符合不递增性质,状态转移
		}
	}
	int ans = -1;
	for (int i=0;i<cnt-1;i++){
   
		//printf("dp[%d] = %d\n",i,dp[i]);
		ans = max(dp[i],ans);
		//最后答案取最大值
	}
	cout<<ans<<endl;
	//求最长递增同理
	for (int i=0;i<cnt-1;i++) dp[i] = 1;
	for (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值