序列 dp

时间:2021/2/22

1. 最大子段和

  对于全是非正数的序列,答案是其中元素的最大值
  对于有正数的序列,考虑以每一个点位结尾的最大子段和,这个子段一定满足其前缀和均非负,因为如果有一个前缀是负的,那么减掉这个前缀对于这个点一定更优,并且这个子段要尽量往前延伸

2. 最长上升子序列 (Longest Increasing Subsequence)

子序列长度越长,结尾元素最小值(记为min)越大

  • 从队列中的第1个数开始枚举,第1个元素作为长度为1的子序列结尾min,存在q[1]中

  • 第二个元素与q比较,插入到单增数列q[x]中,作为长度为x的子序列结尾min

···

在这个过程中,如果枚举到一个小数,会更新某个长度的结尾元素最小值

如:

  • q [1]2 [2]6 [3]11
  • 下一个数是3
  • 则更新成q [1]2 [2]3 [3]11 ,
    因为显然在2的长度下以3结尾比以6结尾具有更大的上升空间

如果枚举到一个大数,一个更大的长度产生并以此数结尾

最后的数组q就是最大上升子序列的一个答案(答案可能不唯一),q的长度就是最大上升子序列的长度

3. 最长公共子序列

输入样例:序列a: acbd   序列b: abedc

枚举到 a 的第 i 个字符和b的第 j 个字符时,f[ i ][ j ]存放最大值,集合划分:i j 选或不选共四种情况,f[ i ][ j ]最终取这四种情况的最大值

1 不选 i 不选 j f[i][j] = f[i - 1][j - 1]

2 不选 i 选 j f[i][j] = f[i - 1][j]

其实f[i - 1][ j ] 真正表示的是: 不选 i , 可能选 j , 也可能不选 j , 由于这里是求最大值,即使真的是不选 i 不选 j 的时候取到最大值,最终结果也还是正确的

3 选 i 不选 j f[i][j] = f[i][j - 1]
4 选 i 选 j f[i][j] = f[i - 1][j - 1] + 1

最后,这四种情况的f[ i ][ j ]取最大值,得新的f[ i ][ j ]
其实,情况 2 3 已经包含了情况 1,所以最终的f[ i ][ j ]只需看情况 2 3 4 的最大值,重新整理一遍思路:

集合分为三部分(1 2 部分有重合)

1 不选 i ,选 j 或 不选 i 不选 j f[i][j] = f[i - 1][j]
2 选 i 不选 j 或不选 i 不选 j f[i][j] = f[i][j - 1]
3 选 i 选 j f[i][j] = f[i - 1][j - 1] + 1 (判断条件:a[i] == b[j])

取 1 2 3 三种情况下f[i][j]的最大值,得真正的f[ i ][ j ]

伪代码:
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ ){
  if (a[i] == b[j])
  f[i][j] = max(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + 1);
  else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
  }

但是这个算法的数据范围是1000,时间复杂度是O( n 2 n^2 n2) , 对于数据范围更大的题目该如何转换思路?

  • 问题描述

给出两个长度为 n 的整数序列,求它们的最长公共子序列(LCS)的长度,保证第一个序列中所有元素都不重复。
注意:
第一个序列中的所有元素均不重复。
第二个序列中可能有重复元素。
一个序列中的某些元素可能不在另一个序列中出现。

  • 输入格式

第一行包含一个整数 n。

接下来两行,每行包含 n 个整数,表示一个整数序列。

  • 输出格式

输出一个整数,表示最长公共子序列的长度。

  • 数据范围

1≤n≤106,
序列内元素取值范围 [1,106]。

  • 输入样例1:

5
1 2 3 4 5
1 2 3 4 5

  • 输出样例1:

5

  • 输入样例2:

5
1 2 3 5 4
1 2 3 4 5

  • 输出样例2:

4

最长公共子序列和最长上升子序列可以相互转化,转化的关键是其中有一个序列没有重复元素

#include <iostream>
#include <cstdio>

using namespace std;
int n;
const int N = 1000010;
int bb[N]<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值