一、总述
线性DP问题是指递推方程具有明显的线性关系,有一维线性和二维线性。
子串和子序列
子串一定一定是连续的,而子序列是整个序列的一部分,可以使不连续的。
二、例题分析
·最长上升子序列1
分析:
算法1-------
算法2-------
算法2的代码-------
#include <iostream> using namespace std; const int N = 1010; int n, cnt; int w[N], f[N]; int main() { cin >> n; for (int i = 0 ; i < n; i++) cin >> w[i]; f[cnt++] = w[0]; for (int i = 1; i < n; i++) { if (w[i] > f[cnt-1]) f[cnt++] = w[i]; else { int l = 0, r = cnt - 1; while (l < r) { int mid = (l + r) >> 1; if (f[mid] >= w[i]) r = mid; else l = mid + 1; } f[r] = w[i]; } } cout << cnt << endl; return 0; }
·最长上升子序列2
题解中最难理解的地方在于栈中序列虽然递增,但是每个元素在原串中对应的位置其实可能是乱的,那为什么这个栈还能用于计算最长子序列长度?
实际上这个栈【不用于记录最终的最长子序列】,而是【以stk[i]结尾的子串长度最长为i】或者说【长度为i的递增子串中,末尾元素最小的是stk[i]】。理解了这个问题以后就知道为什么新进来的元素要不就在末尾增加,要不就替代第一个大于等于它元素的位置。
这里的【替换】就蕴含了一个贪心的思想,对于同样长度的子串,我当然希望它的末端越小越好,这样以后我也有更多机会拓展。#include<iostream> #include<algorithm> #include<vector> using namespace std; int main(void) { int n; cin >> n; vector<int>arr(n); for (int i = 0; i < n; ++i)cin >> arr[i]; vector<int>stk;//模拟堆栈 stk.push_back(arr[0]); for (int i = 1; i < n; ++i) { if (arr[i] > stk.back())//如果该元素大于栈顶元素,将该元素入栈 stk.push_back(arr[i]); else//替换掉第一个大于或者等于这个数字的那个数 *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i]; } cout << stk.size() << endl; return 0; } /* 例 n: 7 arr : 3 1 2 1 8 5 6 stk : 3 1 比 3 小 stk : 1 2 比 1 大 stk : 1 2 1 比 2 小 stk : 1 2 8 比 2 大 stk : 1 2 8 5 比 8 小 stk : 1 2 5 6 比 5 大 stk : 1 2 5 6 stk 的长度就是最长递增子序列的长度 */
·拦截导弹
#include<iostream> #include<algorithm> using namespace std; const int N = 1e5+10; int w[N],dp[N],f[N],n = 1; int main(){ while(scanf("%d",&w[n]) == 1) ++n; int len = 0; dp[0] = 0x3f3f3f3f; for(int i=1;i<=n;++i){ int l = 0 , r = len; while(l < r){ int mid = l + r + 1>> 1; //后一发导弹不高于前一发 if(dp[mid] >= w[i]) l = mid; else r = mid - 1; } len = max(len,l + 1); dp[l+1] = w[i]; } // for(int i=1;i<=len;++i) cout << dp[i] << " "; // cout << endl; cout << len - 1 << endl; int cnt = 1; for(int i=1;i<=n;++i){ int k = 1; while(k < cnt && f[k] < w[i]) ++k; f[k] = w[i]; if(k >= cnt) ++cnt; } cout << cnt - 1 << endl; return 0; }
·导弹防御系统
题目:
分析:
·最长公共子序列
·最长公共上升子序列
#include <cstdio> #include<iostream> #include<algorithm> #include <cstring> using namespace std; const int N = 3010; int f[N][N]; int A[N], B[N]; int n; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &A[i]); for(int i = 1; i <= n; ++i) scanf("%d", &B[i]); memset(f, 0, sizeof f); for(int i = 1; i <= n; ++i) { int maxv = 0; //前j-1个符合B[k]<B[j]的LCIS长度 for(int j = 1; j <= n; ++j) { f[i][j] = f[i - 1][j]; if(A[i] == B[j]) f[i][j] = maxv + 1; if(B[j] < A[i] && maxv < f[i][j]) maxv = f[i][j]; } } int res = 0; for(int j = 1; j <= n; ++j) res = max(res, f[n][j]); printf("%d\n", res); return 0; }