原题链接:点我。
这道题其实是一个变相的最长上升子序列。欲哭无泪啊。
正传:
百度上搜了一下 LCS的求法,然后发现最优的 dp 就是 𝑂(𝑛2)?
但是注意到一个性质 ,两个数组都是 1~ 𝑛 的排列。
所以今天我们要秀出什么操作呢!
如何将原题从LCS(最长公共子序列)变为LIS(最长上升子序列)
因为题中说明是每行为 𝑛 个数,为自然数 1,2,…,𝑛的一个排列。我们可以以第一个串为标准,用第二个串来匹配第一个串,看能匹配多少,所以,其实第一个串的每个数字其实影响不大,只有知道它对应了第二串的哪个数字就好了,那么我们为什么不把他给的串重新定义一下?
比如题中的样例:3 2 1 4 5 我们把它变成 1 2 3 4 5 用一个数组记录一下每个数字变成了什么,相当于离散化了一下3-1;2-2;1-3;4-4;5-5;
现在我们的第二串1 2 3 4 5 按我们离散化的表示:3 2 1 4 5。
如果你还没懂,在举一个栗子
输入:
5
4 3 5 1 2
2 3 5 4 1
4 3 5 1 2 我们把它变成1 2 3 4 5 也就相当于离散化了一下,4-1 ;3-2;5-3;1-4;2-5;
第二串2 3 5 4 1直接就可以表示为5 2 3 1 4。(。◝ᴗ◜。)
懂了吧(没懂的在看几遍例子,别说话),我们把第一个串离散化后的数组是满足上升,反过来,满足上升的也就是满足原串的排列顺序的,O(∩_∩)O~
好了 ,现在的问题就变成了求一个最长不下降序列!代码如下。
for(int i = 1;i <= n;i++){
cin >> a[i];
num[a[i]] = i;//a[]数组中第i项的值离散化后应为i
}//num[]为离散化标记数组,num[某数]就是某数离散化后的结果
for(int i = 1;i <= n;i++){
cin >> b[i];
b[i] = num[b[i]]//a[]数组中第i项的值离散化后应为num[b[i]]
}
时间复杂度优化
看到最长上升子序列,我们一般会想到某种DP(至少我是这样,嘿嘿)。代码如下。
for(int i = 1;i <= n;i++){
for(int j = i;j <= n;j++){
if(a[j]>a[i]){
dp[j] = max(dp[j],dp[i]+1);
}
}
}
我们其实不难看出,对于𝑛^2做法而言,其实就是暴力枚举:将每个状态都分别比较一遍。但其实有些没有必要的状态的枚举,导致浪费许多时间,当元素个数到了10^4以上时,就已经超时了。而此时,我们可以通过另一种动态规划的方式来降低时间复杂度:
将原来的dp数组的存储由数值换成该序列中,上升子序列长度为i的上升子序列,的最小末尾数值
这其实就是一种几近贪心的思想:我们当前的上升子序列长度如果已经确定,那么如果这种长度的子序列的结尾元素越小,后面的元素就可以更方便地加入到上升子序列中。
注:t为dp[]中第一个小于b[i]的数的下标
表达式:
就是每次都向前找比它小的数和比它大的数的位置,将第一个比它大的替换掉,这样操作虽然LIS序列的具体数字可能会变,但是很明显LIS长度还是不变的,因为只是把数替换掉了,并没有改变增加或者减少长度。但是我们通过这种方式是无法求出最长上升子序列具体是什么的,这点和最长公共子序列不同。
我们现在就转化为了求t的值。为了使代码即简洁,复杂度又为O(nlogn)我们选用STL中的;lower_bound()函数(其实是不想打太多)。不会的自行搜查。ヾ(❀^ω^)ノ゙二分查找代码如下:
int len = 0;//len为dp[]数组的长度也是最终的结果
for(int i = 1;i <= n;i++){
if(b[i] > dp[len]) dp[++len] = b[i];
else {
int t = lower_bound(dp+1,dp+len+1,b[i]) - dp;
dp[t] = b[i];
}
}
本道题就完结了!!
完整代码
最后附上完整代码:
#include<bits/stdc++.h>
using namespace std;
int a[100001],b[100001],num[100001],dp[100001];
int main(){
int n;
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i];
num[a[i]] = i;
}
for(int i = 1;i <= n;i++){
cin >> b[i];
b[i] = num[b[i]];
}
int len = 0;
for(int i = 1;i <= n;i++){
if(b[i] > dp[len]) dp[++len] = b[i];
else {
int t = lower_bound(dp+1,dp+len+1,b[i]) - dp;
dp[t] = b[i];
}
}
cout << len << endl;
return 0;
}
制作不易,感谢观看
麻烦dalao们用您金贵的手指点点关注呗