最长递增子序列 题解
题目
题目描述
给出一个数列,求其最长递增子序列.
数据范围
n < = 1 0 5 n <= 10^5 n<=105.
题解
思路
本题应考虑 DP,最朴素的想法便是设 d p [ i ] dp[i] dp[i] 为以 i i i 为结尾的最长子序列的长度,然后对于每一个 s [ i ] s[i] s[i],枚举每一个 j < = s [ i ] j<=s[i] j<=s[i] 的数据,取 d p [ s [ i ] ] = m i n ( d p [ s [ i ] , d p [ j ] + 1 ) dp[s[i]]=min(dp[s[i],dp[j]+1) dp[s[i]]=min(dp[s[i],dp[j]+1).
这样的效率是 O ( n 2 ) O(n^2) O(n2),不足以满足题目要求。
于是我们考虑将效率降为 O ( n l o g n ) O(nlogn) O(nlogn),我们分析数据:
对于某一个长度的子序列,其结尾的数字越小越优(相同的长度,结尾数字越小越可以接更多的数字),而对于相同结尾数字的子序列,其长度约长越优。因此出现了“两重单调性”,我们考虑用单调队列来解决(单调队列的下标为一重单调,数据为另一重单调)。
当我们尝试一个 s [ i ] s[i] s[i] 的时候,用二分查找 d p dp dp 数组中最大的比 s [ i ] s[i] s[i] 小的数(因为单调性,找到的数对应的下表——也就是子串的长度——一定是最长的),然后令 d p [ i ] = m i n ( d p [ i ] , s [ i ] ) dp[i]=min(dp[i],s[i]) dp[i]=min(dp[i],s[i]),最后统计 d p [ i ] dp[i] dp[i];如果 d p dp dp 数组中最大的数也比 s [ i ] s[i] s[i] 小,那么就将 s [ i ] s[i] s[i] 插入 d p dp dp 数组的最后一位数后面,让 d p dp dp 数组增加一个数。最后答案便是 d p dp dp 数组的长度。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n, b[N], to[N];
int dp[N], que[N], p;//dp[i]:长度为i的最长子串的最小结尾
int main(){
// freopen("input.txt", "r", stdin);
scanf("%d", &n);
for(int i=1; i<=n; ++i){
int a; scanf("%d", &a);
to[a] = i;
}
for(int i=1; i<=n; ++i){
scanf("%d", b+i);
b[i] = to[b[i]];
}
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for(int i=1; i<=n; ++i){
if(dp[p]<b[i]){
dp[++p] = b[i];
continue;
}
int l = 1, r = p;
while(l<r){
int mid = (l+r)>>1;
if(dp[mid]<b[i])
l = mid+1;
else
r = mid;
}
dp[l] = min(dp[l], b[i]);
}
printf("%d", p);
return 0;
}