给出一个数n,然后给出n个数,求n个数的最长单调子序列。如:
n = 7
7 1 5 2 9 3 6
方法一:DP
dp[i] 表示元素值不超过i时的最长单调序列。时间复杂度为O(n^2)则可以这样:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 100000 int a[N]; int dp[N]; int main() { //freopen("in.txt", "r", stdin); int n; while(~scanf("%d", &n)) { int i, j; for(i=0; i<n; i++){ scanf("%d", &a[i]); dp[i] = 1; } int max = -(1<<30); for(i=0; i<n; i++){ for(j=0; j<i; j++){ if(a[i] > a[j] && dp[i] < dp[j] + 1) dp[i] = dp[j] + 1; } if(dp[i] > max) max = dp[i]; } printf("%d\n", max); } }
其中c[i] 表示长度为i的单调递增子序列的最后一个元素。在遍历过程c[i]取值可能有多个,则取值最小的一个。这样c数组中的元素就会是递增的。所以可以用二分查找。
比如之前的例子:
n = 7
7 1 5 2 9 3 6
a[] 0 1 2 3 4 5 6
核心过程:
对c数组初始化为一个不可能出现的数。
先令len = 1;c[len] = a[0];
之后在c数组中遍历查找a[i] (1 < i < n),如果a[i] > c[len];就在c数组末尾添加,即是c[++len] = a[i];如果找到了a[i],就不做什么,如找不到,就把c数组中第一个比a[i] 大的数替换成a[i],最后,c数组的长度len就是a数组的最长单调子序列长度!
0 1 2 3 4 5 6 7 (c数组下标)
a[0] -> c -1 7
a[1] -> c -1 1
a[2] -> c -1 1 5
a[3] -> c -1 1 2
a[4] -> c -1 1 2 9
a[5] -> c -1 1 2 3
a[6] -> c -1 1 2 3 6
所以len = 4
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 100010 int c[N]; int ans; void bsearch(int *A, int l, int r, int key) { if(key > A[r]){ ans++; A[ans] = key; return; } int mid; while(l <= r) { mid = l + (r - l) / 2; if(A[mid] == key) return; else if(A[mid] > key) r = mid - 1; else l = mid + 1; } A[l] = key; } int main() { //freopen("in.txt", "r", stdin); int n, a; while(~scanf("%d", &n)) { ans = 1; memset(c, 0, sizeof(c)); int i, j; scanf("%d", &a); c[1] = a; for(i=1; i<n; i++){ scanf("%d", &a); bsearch(c, 1, ans, a); } printf("%d\n", ans); } return 0; }