对应 POJ 题目:点击打开链接
给出一个数n,然后给出n个数,求n个数的最长单调子序列。如:
n = 7
7 1 5 2 9 3 6
方法一:DP
// 把下面的注释去掉就可以打印路径
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100010
int a[N];
int dp[N];
//int index[N];
//int sub[N];
int main()
{
//freopen("in.txt", "r", stdin);
int n;
while(~scanf("%d", &n))
{
int i, j;
//int beg_index = 0;
for(i = 0; i < n; i++){
scanf("%d", &a[i]);
dp[i] = 1;
//index[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;
//index[i] = j;
}
}
if(dp[i] > max){
max = dp[i];
//beg_index = i;
}
}
printf("%d\n", max);
/*
for(i = beg_index, j = max-1; i != -1; i = index[i])
sub[j--] = a[i];
for(i = 0; i < max; i++)
printf("%d ", sub[i]);
printf("\n");
*/
}
}
dp[i] 表示元素值不超过i时的最长单调序列。时间复杂度为O(n^2)则可以这样:
方法二:引入一个c数组 + 二分查找,时间复杂度为O(nlg(n))。
其中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 a[N];
int c[N];
//int b[N];
//int index[N];
//int sub[N];
int bsearch(int *A, int l, int r, int key)
{
if(key > A[r]){
return r + 1;
}
int mid;
while(l <= r)
{
mid = l + (r - l) / 2;
if(A[mid] == key) return mid;
else if(A[mid] > key) r = mid - 1;
else l = mid + 1;
}
return l;
}
int main()
{
//freopen("in.txt", "r", stdin);
int n;
while(~scanf("%d", &n))
{
int ans = 1;
int i, j;
for(i = 0; i < n; i++)
scanf("%d", a + i);
c[0] = a[0];
//b[0] = 0;
//index[0] = -1;
for(i = 1; i < n; i++){
int pos;
pos = bsearch(c, 0, ans-1, a[i]);
c[pos] = a[i];
/*
b[pos] = i;
if(pos > 0)
index[i] = b[pos-1];
else
index[i] = -1;
*/
if(pos == ans)
ans++;
}
printf("%d\n", ans);
/*
for(i = b[ans-1], j = ans-1; i != -1; i = index[i])
sub[j--] = a[i];
for(i = 0; i < ans; i++)
printf("%d ", sub[i]);
printf("\n");
*/
}
return 0;
}