poj 2533 关于最长上升子序列的问题
以poj 2533为例
dp里面的经典问题 最长上升子序列
经典的方法, d[i]表示以第i个数结尾的最长上升子序列长度
d[j] = max{d[i] + 1 | i < j } 最终答案为d[j]中的最大值, 从小到大扫描即可完成, 一共两重循环
复杂度为O(n^2)
复杂度更低的优化的方法, d[i]数组不变, 增加一个新的数组c[i],表示长度为i的上升子序列的结尾的数的值
许多文章当中都有针对这个问题的证明: 对于寻找以某个数结尾的上升子序列, 那么这儿数之前的长度相同的子序列, 更优化的选择
是选择位置靠前的。
例如两个状态 d[a] = d[b] a < b, 对于后续的选择 d[a] 要比 d[b] 更有优势
所以对于长度相同(即d[]相同)的序列, 都取靠近左侧的值即可
外面的循环不变,内部循环采用二分查找的方法直接找到对于A[i]合适的C[j], 更新d的数值和C[j]的数值, 这里使用STL的lower_bound会更为简洁
poj 2533
两种不同方法的实现, 不同之处主要在于内部的循环, 前者为n此遍历, 后者为二分查找logn
#include <iostream> #include <vector> #include <map> #include <list> #include <set> #include <deque> #include <stack> #include <queue> #include <algorithm> #include <cmath> #include <cctype> #include <cstdio> #include <iomanip> #include <cmath> #include <cstdio> #include <iostream> #include <string> #include <sstream> #include <cstring> #include <queue> using namespace std; ///宏定义 const int INF = 990000000; const int MAXN = 100010; const int maxn = MAXN; ///全局变量 和 函数 int n; int A[maxn]; int d[maxn]; //dp中以第i个数结尾的最长上升序列的数值 int c[maxn]; //最长上升序列长度为i的最后一个结尾数的数值 int ans; int main() { ///变量定义 while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; i++) { scanf("%d", &A[i]); d[i] = 1; } ans = 1; //o(n^2)的方法 /* for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (A[i] > A[j]) { d[i] = max(d[i], d[j] + 1); } } } */ //o(nlgn)的方法 //初始化C数组 for (int i = 0; i <= n; i++) c[i] = INF; for (int i = 0; i < n; i++) { int k = lower_bound(c + 1, c + n + 1, A[i]) - c; d[i] = k; c[k] = A[i]; } for (int i = 0; i < n; i++) ans = max(ans, d[i]); printf("%d\n", ans); } ///结束 return 0; }