(一)O(n^2)算法
相信你已经掌握了。
【Code】严格的最长递增子序列
for(int i=1; i<=N; i++) {
f[i] = 1;
for(int j=1; j<i; j++)
if(a[j] < a[i] && f[i] < f[j] + 1) f[i] = f[j] + 1;
ans = max(ans, f[i]);
}
(二)O(n logn)算法
用数组d,d[i]表示LIS为i的这个子序列末尾是谁(有多个取最小值)
当前LIS长度为len,如果A[i] > D[len] 就加到这个LIS末尾,len++
否则用二分查找找到一个刚好满足的D[k]。
思考二分查找的正确性:D数组一定是递增的。D的下标是LIS的长度,因此长度为1的LIS结尾一定不大于长度为2的LIS结尾.
【Code】不严格LIS
#include <iostream>
using namespace std;
#define DEBUG 0 //可以打印D数组帮助思考
int Search(int *a, int n, int w) {
int l = 1, r = n, ans = -1, mid;
while(l <= r) {
mid = l + r >> 1;
if(a[mid] <= w) l = (ans = mid) + 1;
else r = mid - 1;
}
return ans;
}
int a[100010], d[100010];
int N, len;
int main() {
scanf("%d", &N);
for(int i=1; i<=N; i++) scanf("%d", &a[i]);
len = 1;
d[1] = a[1];
for(int i=2; i<=N; i++) {
if(a[i] >= d[len]) d[++len] = a[i];
else {
int pos = Search(d, N, a[i]);
d[pos] = a[i];
}
}
if(DEBUG) for(int i=1; i<=len; i++) cout << d[i] << ' ';
cout << len << endl;
return 0;
}
仔细想想,好像并不是DP,只是一个二分。(Orz)