求最長上升子序列長度:
單純的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;
}