【Tai_mount】 算法学习 - 线性动态规划 - 最长上升子序列/最长公共子序列
导弹拦截
前者题意即为求最长不上升子序列和最长上升子序列
想一想应该就知道了。
看的是导弹拦截第一篇题解的方法。
先以最长不上升子序列为例。
我们有一个数组seq储存要处理的数列,另建一个同样大的数组ord。
从1到n一个个遍历seq中的元素,判断是否小于等于ord目前的最后一个元素。(ord初始是一个空的数列,逐渐被填满的),如果满足就往里面塞。不满足就它去替换序列里第一个小于他的元素。
这里面找到第一个小于他的元素是用STL实现的,后面再介绍。这个方法的逻辑在于:
- 如果一直满足这显然就是个不上升的子序列
- 如果不满足了,我们把这个数替换进去,就相当于前面大于等于这个数的序列还可以保留使用,从这个位置开始继续累积。而同时,可以同时有很多个这样的序列也在积累。其实就是这一个序列里同时判断了很多
啊啊啊啊啊啊啊我发现我真的很难描述,我我我我我我这东西真的靠自己理解。
算了反正大部分都是写给自己复习的,能看懂!
upper_bound找的是不带等号的,lowerbound找的是带等号的。参数是:要找的序列的第一个元素的地址,要找的序列的最后一个元素的后面一个元素的地址,设置的那个作为阈值的数,cmp函数。
默认是要求序列升序(一定是有序的啊!),这时找第一个大于那个数的。cmp和sort里面的差不多,可以找第一个小于那个数的,此时要求序列降序。
返回值是你要找的那个位置的指针。
Veritas跟我说的是,因为指针会有奇奇怪怪的错误,所以用这个指针减去数组的地址,就可以直接得到下标,这样不会出问题。
然后upper_bound和lower_bound是用的二分查找的办法,相信屏幕面前的自己应该是可以想明白的。也是logn的复杂度这样。
所以总的复杂度是nlogn。
上升下降不上升不下降……自己想一想全都能写出来了。
最长公共子序列
我是连着做这两道题的,在luogu的同一个题单里,所以我就想到用上面的方法解决下面的题。
给的两个序列是1n的不重复的数字,比如说给的是A序列和B序列吧。1n在A序列中都有一个唯一的位置。我们把这个位置信息替换B序列里实在的数——那么这道题就变成了求最长上升子序列了。
妙啊!
用upper和lower都可以,反正不会有重复的数
代码Tips
//导弹拦截
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100007,MAXN=50007;
int seq[N],n;
void input(){
// cin>>n;
// for(int i=1;i<=n;i++){
// cin>>seq[i];
// }
int x;
while(cin>>x){
n++;
seq[n]=x;
}
}
int NoUpfun(){
int len=0,ord[N];
memset(ord,0,sizeof(ord));
ord[0]=MAXN;
for(int i=1;i<=n;i++){
if(ord[len]>=seq[i]){
len++; ord[len]=seq[i];
continue;
}
int p=upper_bound(ord+1,ord+len,seq[i],greater<int>())-ord;
ord[p]=seq[i];
}
return len;
}
int Upfun(){
int len=0,ord[N];
memset(ord,0,sizeof(ord));
for(int i=1;i<=n;i++){
if(ord[len]<seq[i]){
len++; ord[len]=seq[i];
continue;
}
int p=lower_bound(ord+1,ord+len+1,seq[i])-ord;
ord[p]=seq[i];
}
return len;
}
void output(){
cout<<NoUpfun()<<'\n'<<Upfun();
}
int main(){
input();
output();
return 0;
}
最长公共子序列
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100007;
int n,base[N],seq[N],ord[N];
void input(){
cin>>n;
int x;
for(int i=1;i<=n;i++){
cin>>x;
base[x]=i;
}
for(int i=1;i<=n;i++){
cin>>x;
seq[i]=base[x];
}
}
int Upfun(){
int len=0;
for(int i=1;i<=n;i++){
if(ord[len]<seq[i]){
ord[++len]=seq[i];
continue;
}
int p=upper_bound(ord+1,ord+len+1,seq[i])-ord;
ord[p]=seq[i];
}
return len;
}
//int Upfun(){
// int len=0;
// for(int i=1;i<=n;i++){
// if(ord[len]<seq[i]){
// len++; ord[len]=seq[i];
// continue;
// }
// int p=lower_bound(ord+1,ord+len,seq[i])-ord;
// ord[p]=seq[i];
// }
// return len;
//}
void output(){
cout<<Upfun();
}
int main(){
input();
output();
return 0;
}
一开始我STL函数里参数都写的ord,ord+len,后面那个其实应该是ord+len+1。但莫名的都没错。其实差别就在于没有判断最后一个元素……
以最长不上升为例,进到这个步骤的元素(比如叫x),x>ord[len]。我们要找的是第一个<x的元素,所以……这个元素永远都不会是满足条件,所以虽然我写错了但并没有完全写错。
……
另外是一度把len写成了n,调了好久。
另外把len++; ord[len]=seq[i]改成ord[++len]=seq[i]会更快
最后我没看出来这是动态规划……

浙公网安备 33010602011771号