求最长上升子序列长度:
单纯的dp时间复杂度是O(n*n)的
dp[i] = max(dp[j]+1); (0=<j<=i-1 && a[i]>a[j])
用二分可以减少查找的时间:时间复杂度:O(n*log(n))
模板:
#define maxn 100010 int a[maxn], b[maxn]; // 二分在b[] 数组里找第一个比num 大的数的位置。 int search_(int num, int low, int high) { int mid; while(low <= high) { mid = (low+high)/2; if (num >= b[mid]) low = mid + 1; else high = mid - 1; } return low; } int LIS(int n) { int i, len, pos; b[1] = a[1]; len = 1; for (i=2; i<=n; ++i) { if (a[i] > b[len]) {// 如果a[i]比b[]中最大的数还大直接插入到最后。 //如果是非递减序列,改为 >= 即可。 len = len + 1; b[len] = a[i]; } else { pos = search_(a[i], 1, len); b[pos] = a[i]; } } return len; }
Eg:题目链接:The All-purpose Zero
题意:给一个序列,序列里的0可以代替任何数,问这个序列里最长递增子序列的长度。0也可以代替负数。(如果不可以的话...)
思路:因为0可以代替任何数,所以ans一定是优先选择0的,然后把每个数减掉它前面的0的个数。为什么减0呢... 比如:1 2 0 3 优先选0,3-1=2,... ,这样就变成了1 2 2 ...求最长上升子序列的长度+0的个数。
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define maxn 100010 int a[maxn], b[maxn]; // 二分在b[] 数组里找第一个比num 大的数的位置。 int search_(int num, int low, int high) { int mid; while(low <= high) { mid = (low+high)/2; if (num >= b[mid]) low = mid + 1; else high = mid - 1; } return low; } int LIS(int n) { int i, len, pos; b[1] = a[1]; len = 1; for (i=2; i<=n; ++i) { if (a[i] > b[len]) {// 如果a[i]比b[]中最大的数还大直接插入到最后。 //如果是非递减序列,改为 >= 即可。 len = len + 1; b[len] = a[i]; } else { pos = search_(a[i], 1, len); b[pos] = a[i]; } } return len; } int main() { //freopen("in.cpp", "r", stdin); int t; scanf("%d", &t); int cas = 0; while(t--) { int n; scanf("%d", &n); int zeroNum = 0, cnt = 0; for (int i=0; i<n; ++i) { int temp; scanf("%d", &temp); if (temp == 0) zeroNum++; else { temp -= zeroNum; a[++cnt] = temp; } } int ans = LIS(cnt) + zeroNum; if (cnt == 0) ans -= 1; printf("Case #%d: %d\n", ++cas, ans); } return 0; }