最长上升子序列可以说是一道动态规划(n^2解法)的经典题目了吧,很早前就A掉了,也没有再研究,最近才发型最长上升子序列问题竟然有nlogn解法。
仔细研究了一下,发现解法中的一个思维的确非常巧妙,今天在此详细讲解一下,简单说一下我的理解。
先上代码,先简单看一下程序就行,讲解在后面
#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 1010;
int a[MAXN], b[MAXN];
//用二分查找的方法找到一个位置,使得num>b[i-1] 并且num<b[i],并用num代替b[i]
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 DP(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 { //用二分的方法在b[]数组中找出第一个比a[i]大的位置并且让a[i]替代这个位置
pos = Search(a[i], 1, len);
b[pos] = a[i];
}
}
return len;
}
int main()
{
int T, N;
scanf("%d",&T);
while(T--){
scanf("%d",&N);
for(int i = 1; i <= N; i++){
scanf("%d", &a[i]);
}
printf("%d\n", DP(N));
if(T)
printf("\n");
}
return 0;
}
我先来举个例子吧,输入
1 5
4 7 2 8 9
其中a为输入数组,b为答案数组(但有一点区别)
第一步:7 > 4,b添一个7
第二步:(比较巧妙了)2小于7,用二分的方法在b[]数组中找出第一个比2大的位置并且让2替代这个位置,结果b成了2,7(可以看出并不是子序列,稍后详细说明)
第三步:8 > 7,b添一个8
第四步:9 > 8,b添一个9,答案为4
可以发现此时b为2,7,8,9,乍一看根本就不是子序列,应该是4,7,8,9才对。
但其实第二步就巧在这个地方,比方如果之后碰到了2,3这种更友解的情况可以直接更迭4,但如果没有其实2也就指代了4。这样一种指代的思想非常巧妙。
还有不解的朋友我再提供两组数(自己凑的特殊数)
2
8
4 7 2 3 4 5 6 9
9
1 7 5 3 6 4 8 9 7
例题
https://nanti.jisuanke.com/t/17319
声明一下:版子来源kuangbin