最长不下降子序列(Longest Increasing Sequence):在一个数字序列中,找到一个最长的子序列(可以不连续),使得这样的子序列是不下降(即非递减)的。
例如序列 a = {1, 2, 3, -1, -2, 7, 9},它的最长不下降子序列是 {1, 2, 3, 7, 9} 长度为5,{1, 2, 3} 和 {-2, 7, 9} 也是非递减序列但不是最长的。
动态规划求解:dp[i] 表示以 a[i] 结尾的最长不下降子序列的长度。于是对 a[i] 来说就有两种可能:
- 在 a[i] 前存在一个数 a[j](即 j < i ),使得 a[j] <= a[i],那说明 a[i] 可以跟在 a[j] 的后面形成一个新的不降序列,此时,dp[i] 是否要更新则取决于 dp[j] + 1 是否大于 dp[i]。(dp[j] + 1 表示以 a[j] 结尾的LIS加上 a[i] 这个元素后的长度)
- 在 a[i] 前的所有元素都比它大,那么以 a[i] 只能自己形成一个LIS,所以 dp[i] = 1。
所以,以 a[i] 结尾的最长不下降子序列的长度就是上面两种结果中较大的那个。状态转移方式:
$$
dp[i] = max \left ( 1, \ dp[j] + 1 \right )
\left ( j = 1,2,\cdots,i-1\ and\ a[j]< a[i] \right )
$$
写出代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <vector> using namespace std; const int N = 110; int main() { int n, a[N], dp[N], ans = -1; cin >> n; for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) { dp[i] = 1; for (int j = 0; j < i; j++) { if (a[i] >= a[j] && dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; } } ans = max(ans, dp[i]); } cout << ans; return 0; }
|
输入:
1 2 3
| 8 1 2 3 -9 3 9 0 11 1 2 3 3 9 11
|
输出:
这里主要问题是得到LIS的长度,但是要做一个好的程序员如果不把这个序列输出来,那是真是叫人悲伤。
解决方案:因为可能不止一个序列满足最长,因此用邻接表的方式,声明vector<int> pre[N]
把所有符合条件的序列用前驱指针的方式存起来。vector<int> dest
存储可以形成LIS的终点结点的索引。
之后只需要用 DFS,遍历一下 pre 数组就行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void dfs(int u) { path.push_back(u); if (pre[u].size() == 0) { for (int i = path.size() - 1; i >= 0; i--) { printf("%d", a[path[i]]); if (i != 0) printf(" "); } printf("\n"); path.pop_back(); return; } for (auto it : pre[u]) dfs(it); path.pop_back(); }
|
完整实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <iostream> #include <vector> using namespace std; const int N = 110; int n, a[N], dp[N], maxlen = -1, ansIndex = 0; vector<int> pre[N], path, ans; void dfs(int u) { path.push_back(u); if (pre[u].size() == 0) { for (int i = path.size() - 1; i >= 0; i--) { printf("%d", a[path[i]]); if (i != 0) printf(" "); } printf("\n"); path.pop_back(); return; } for (auto it : pre[u]) dfs(it); path.pop_back(); } int main() { cin >> n; for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) { dp[i] = 1; for (int j = 0; j < i; j++) { if (a[i] >= a[j]) { if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; pre[i].clear(); pre[i].push_back(j); } else if (dp[j] + 1 == dp[i]) { pre[i].push_back(j); } } } if (dp[i] > maxlen) { maxlen = dp[i]; ans.clear(); ans.push_back(i); } else if (dp[i] == maxlen) { ans.push_back(i); } } for (auto it : ans) dfs(it); return 0; }
|
输入:
输出:(这里只有一个子串满足LIS)