关于『子序列』(初阶)

关于『子序列』(初阶)

子序列的几大问题:
  • 最长上升子序列( L C S LCS LCS)
  • 最长公共子序列( L I S LIS LIS)
  • 最长公共上升子序列( L C I S LCIS LCIS)
  • 最长不下降子序列
1.最长上升子序列
朴素算法(n²)

子序列系列的问题都可以用 动态规划 来实现。

对于最长上升子序列,定义如下:

最长上升子序列:指一个序列中最长的 单调递增 的子 序列

注意序列与串的区别。

那么我们知道动态规划的灵魂在于 决策(也称状态转移方程),那么该问题的决策是什么?

要搞清具体的决策,首先要考虑清楚 阶段状态 两个东西。

我们先定义一个状态 d p [ i ] dp[i] dp[i],表示前 i i i 个数以 a [ i ] a[ i ] a[i] 结尾的最长上升子序列长度。

举个例子:

我们有一个序列(无序):

3 , 4 , 1 , 5 , 6 , 3 , 6 , 8 , 9 , 12 , ⋯ 3,4,1,5,6,3,6,8,9,12,\cdots 3,4,1,5,6,3,6,8,9,12,

i = 1 i = 1 i=1 时, d p [ 1 ] = 1 dp[1] = 1 dp[1]=1,子序列为 3 3 3

i = 2 i = 2 i=2 时, d p [ 2 ] = d p [ 1 ] + 1 = 2 dp[2] = dp[1]+1 = 2 dp[2]=dp[1]+1=2,子序列为 3 , 4 3,4 3,4

i = 3 i = 3 i=3 时, d p [ 3 ] = 1 dp[3] = 1 dp[3]=1,子序列为 1 1 1 。(在 1 1 1 之前没有比 1 1 1 小的,故自身为子序列)

i = 4 i = 4 i=4 时, d p [ i ] = d p [ 2 ] + 1 = 3 dp[i] = dp[2]+1=3 dp[i]=dp[2]+1=3,子序列为 3 , 4 , 5 3,4,5 3,4,5

⋯ \cdots

可以得到结论:

d p [ i ] = max ⁡ { d p [ j ] } + 1 , 1 ⩽ j < i , a [ j ] < a [ i ] dp[i]=\max\{dp[j]\} + 1,\quad 1 \leqslant j < i,\quad a[j]<a[i] dp[i]=max{ dp[j]}+1,1j<i,a[j]<a[i]

代码实现:

/*LCS*/
#include <cstdio>
int a[5005], dp[5005], prev[5005], ans, Max = -1;
void print(int i) {
    /*递归输出,比较巧妙(可以画个递归树看看)*/
	if(prev[i] == i) {
   
		printf("%d", a[i]);
		return;
	}
	print(prev[i]);
	printf(" %d", a[i]);
    return;
}
int main() {
   
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
   
		scanf("%d", &a[i]);
		dp[i] = 1;
		prev[i] = i; /*前缀和数组,减少时间复杂度*/
	}
	for(int i = 2; i <= n; i ++) {
   
		for(int j = 1; j < i; j ++) {
   
			if(a[i] > a[j] && dp[i] < dp[j] + 1) {
    /*套公式*/
				dp[i] = dp[j] + 1;
				prev[i] = j;
			} 
		}
	}
	for(int i = 1; i <= n; i ++) {
    /*得最大值*/
		if(Max < dp[i]) {
   
			Max = dp[i];
			ans = i;
		}
	}
	printf("%d\n", Max);
	print(ans);
    return 0;
}

当然, L C S LCS LCS 大多数情况下 并不是唯一的 ,如果没有 S P J SPJ SPJ 的话,则需要好好揣摩题目的输出规律。这里给出一种。

二分(nlog n)

我们可以把求最长上升子序列的过程看做一个

上面介绍了 L C S LCS LCS 的动态规划做法。还有二分的,贪心的,树状的,还有用 l o w e r _ b o u n d lower\_bound lower_bound 的;有 O ( n 2 ) O(n^2) O(n2) 的,有 O ( n 3 ) O(n^3) O(n

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值