1.最大连续子序列和
问题定义
即求一个连续子序列,使的其和最大。例如对于序列{5,-3,4,2}
来说,最大子序列为{5,-3,4,2}
,和为8
。对于序列{5,-6,4,2}
,最大子序列为{4,2}
,和为6
。
因为是求 连续序列,所以我们以 S i S_{i} Si 表示问题状态,代表以 A i A_{i} Ai 结尾
的子序列的最大和,则我们有下列动态规划递推:
即若前 i i i 项的和不小于0的时候,我们可以接着扩展;否则从第 i i i 项开始另开一个新的子序列。同时,从 S i S_{i} Si 中找最大值。
实现示例
int MSS1(int* num, int len) {
// 每个前缀Ai对应的最大和存储在数组dp[i]中
int* dp = new int[len];
dp[0] = num[0];
for (int i = 1; i < len; i++) {
// 状态转移方程
dp[i] = max(dp[i - 1] + num[i], num[i]);
}
int maxsum = dp[0];
for(int i=1;i<len;i++){
maxsum = maxsum > dp[i] ? maxsum : dp[i];
}
return maxsum;
}
int MSS2(int* num, int len) {
int sum = num[0]; // 这里的sum就代替了dp[i]来求最大值
int maxsum = num[0];
for (int i = 1; i < len; i++) {
if (sum >= 0) sum += num[i];
else sum = num[i];
if (sum > maxsum) maxsum = sum;
}
return maxsum;
}
2.最长递增子序列
问题定义
最长递增子序列(Longest Increasing Subsequence,简写 LIS),给定一个序列 L = { A 1 , A 2 , . . . , A n } L=\{A_{1},A_{2},...,A_{n}\} L={
A1,A2,...,An}我们需要找到一个子序列 L i n = { A k 1 A k 2 . . . A k m } L_{in} = \{ A_{k1}A_{k2}...A_{km}\} Lin={
Ak1Ak2...Akm},使得 k 1 < k 2 < . . . < k m k_{1} < k_{2} < ...< k_{m} k1<k2<...<km 且 A k 1 < A k 2 < . . . < A k m A_{k1} < A_{k2} < ... < A_{km} Ak1<Ak2<...<Akm,即求解 最长上升子序列。
同样的,我们以 L i L_{i} Li 表示以 A i A_{i} Ai 结尾的子序列的最长长度,则有递推式:
即,对于 A i A_{i} Ai 我们将其尽可能的接到以 A j A_{j} Aj结尾的子序列上,然后求最长长度。注意每个元素在刚开始自成一个上升子序列,即初始化L[i] = 1
。
实现示例
int LIS(int* num, int len) {
// 复杂度O(n^2)
int* dp = new int[len];
dp[0] = 1;
for (int i = 1; i < len; i++) {
dp[i] = 1; // 初始化为1
for (int j = 0; j < i; j++) {
if (num[j] < num[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int maxlen = dp[0];
for (int i = 1; i < len; i++) {
maxlen = maxlen > dp[i] ? maxlen : dp[i];
}
delete[] dp;
return maxlen;
}
3.最长公共子序列
问题定义:
假设有两个序列 A n = { a 0 a 1 a 2 . . . a n − 1 } A_{n} = \{ a_{0}a_{1}a_{2}...a_{n-1}\} An={
a0a1a2...an−1} 和 B m = { b 0 b 1 b 2 . . . b m − 1 } B_{m} = \{b_{0}b_{1}b_{2}...b_{m-1}\} Bm={
b0b1b2...bm−1}则我们定义一个公共子序列(非连续
) Z k = { z 0 z 1 z 2 . . . z k − 1 } Z_{k} = \{z_{0}z_{1}z_{2}...z_{k-1}\} Z