【知识点3】最长不下降子序列(LIS)⭐⭐⭐⭐⭐

今天也是为了cc,努力奋斗的一天ヾ(≧▽≦*)o

问题描述

在这里插入图片描述

暴力解决方案

时间复杂度为 O ( 2 n ) O(2^n) O(2n)
在这里插入图片描述

动态规划解决方案

事实上,上述的枚举过程包含了大量重复计算。通过学习下面的动态规划解法应该就会很容易理解为什么会有重复计算产生了。

步骤1——数据结构定义

dp[i]表示以A[i]结尾的最长不下降子序列长度。

这样对A[i]来说就会有两种可能:
在这里插入图片描述
下面是一个更有趣的例子:
在这里插入图片描述
在这里插入图片描述

步骤2——状态转移方程

通过步骤1的分析,可以得到下面的状态转移方程:

dp[i] = max{1,dp[j]+1}(j=1,2,..,i-1&&A[j]<=A[i])

上面的状态转移方程隐含了边界dp[i]=1(1≤i≤n)。整体复杂度为 O ( n 2 ) O(n^2) O(n2)

到此就可以想象究竟重复计算出现在哪里了:每次碰到子问题“以A[i]结尾的最长不下降子序列”时,都去重新遍历所有子序列,而不是直接记录这个子问题的结果。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 100;
int A[N],dp[N];

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&A[i]); 
	}
	
	int ans = -1;	//记录最大的dp[i]
	for(int i=1;i <= n;++i){
		dp[i] = 1;	//边界初始条件(即先假设每个元素自成一个子序列)
		for(int j=1;j<i;++j){
			if(A[i] >= A[j] && (dp[j]+1>dp[i])){
				dp[i] = dp[j] + 1;	//状态转移方程,用以更新dp[i] 
			}
		}
		ans = max(ans,dp[i]); 
	}
	printf("%d",ans); 
	return 0;
}

题型训练

  1. 拦截导弹
  2. 合唱队形
    比较抽象一定的类LIS问题,需要进行一些排序操作,确保遍历时,子问题一定在原问题前面:
  3. LeetCode 1048. Longest String Chain
  4. LeetCode 435. Non-overlapping Intervals
  5. LeetCode 674. Longest Continuous Increasing Subsequence

参考文档

  1. 算法笔记
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值