您的位置:首页 > 其它

[dp]最长单调递增子序列LIS

2017-05-12 22:10 295 查看

https://www.51nod.com/tutorial/course.html#!courseId=12

解题关键:

如果将子序列按照长度由短到长排列,将他们的最大元素放在一起,形成新序列$B\left\{ {{b_1},{b_2}, \ldots  \ldots ,{b_j}} \right\}$,则序列$B$满足${b_1} < {b_2} <  \ldots  \ldots  < {b_j}$。这个关系比较容易说明,假设${b_{xy}}$表示序列A中长度为$x$的递增序列中的第$y$个元素,显然,如果在序列$B$中存在元素${b_{mm}} > {b_{nn}}$,且$m < n$则说明子序列${B_n}$的最大元素小于${B_m}$的最大元素,因为序列是严格递增的,所以在递增序列${B_n}$中存在元素${b_{nm}} < {b_{nn}}$,且从${b_{n0}}$到${b_{nm}}$形成了一个新的长度为$m$的递增序列,因为${b_{mm}} > {b_{nn}}$,所以${b_{mm}} > {b_{nm}}$,这就说明在序列$B$中还存在一个长度为$m$,最大元素为${b_{nm}} < {b_{mm}}$的递增子序列,这与序列的定义,${b_{mm}}$是所有长度为m的递增序列中第$m$个元素最小的序列不符,所以序列$B$中的各元素严格递增。

 

注意liss存的是下标,主要是为了求pre用,若只求max,你当然可以设成值。 

爆炸了,刚发现《挑战竞赛程序设计》上有一个代码非常非常简短的模板,炸了;

STL模板:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
using namespace std;

const int N=131072;
int n=7, a
 = {0,0,1,1,0,0,2};
template<class Cmp>
int LIS (Cmp cmp){
static int m=0,end
;
for(int i=0;i<n;i++){
int pos=lower_bound(end,end+m,a[i],cmp)-end;
end[pos]=a[i],m+=pos==m;
}
return m;
}
bool greater1(int value){
return value>=1;
}

int main(){
cout<<LIS(less<int>())<<endl;         //严格上升
cout<<LIS(less_equal<int>())<<endl;   //非严格上升
cout<<LIS(greater<int>())<<endl;      //严格下降
cout<<LIS(greater_equal<int>())<<endl;//非严格下降
cout<<count_if(a,a+7,greater1)<<endl; //计数
//第二个参数为末尾元素的下一个位置
return 0;
}

 

最终模板:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
ll a[50002],dp[50002];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
fill(dp,dp+n,INF);
for(ll i=0;i<n;i++){
*lower_bound(dp,dp+n,a[i])=a[i];
}
printf("%lld\n",lower_bound(dp,dp+n,INF)-dp);
}

 

自己改进模板(不带路径):注意二分时上界为len+1就ok了,也可以fill成inf,更简单明了

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int arr[50002],dp[50002];
int n,len;
int find1(int x){
int mid,l=1,r=len+1;
while(l<r){
mid=(l+r)>>1;
if(dp[mid]>x) r=mid;
else l=mid+1;
}
return r;
}
int lis(){
int dex;
for(int i=1;i<=n;i++){
dex=find1(arr[i]);
dp[dex]=arr[i];
len=max(len,dex);
}
return len;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>arr[i];
}
ll ans=lis();
printf("%lld\n",ans);
return 0;
}

 

 

 

 带路径模板1

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
int a[50002],dp[50002],pre[50002],res[50002],n;
int len;

int find1(int x){
int mid,l=1,r=len+1;
while(l<r){
mid=(l+r)>>1;
if(a[dp[mid]]>x) r=mid;
else l=mid+1;
}
return r;
}

int lis(){
len=0;
for(int i=1;i<=n;i++){
int dex=find1(a[i]);
dp[dex]=i;
if(dex!=1) pre[i]=dp[dex-1];
len=max(len,dex);
}

int k=dp[len],t=len;
while(pre[k]!=k){
res[t--]=a[k];
k=pre[k];
}
res[t]=a[k];
return len;
}
int main(){
cin>>n;
for(int i=0;i<=n+2;i++)    pre[i]=i;
for(int i=1;i<=n;i++)    cin>>a[i];

ll ans=lis();
printf("%lld\n",ans);
for(int i=1;i<=ans;i++){
printf("%d ",res[i]);
}
printf("\n");
}

 

 带路径模板2

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
int arr[50002],liss[50002],pre[50002],res[50002];
int find1(int i,int l,int r){
int mid;
while(l<r){
mid=(l+r)>>1;
if(arr[liss[mid]]>arr[i]) r=mid;
else l=mid+1;
}
return r;
}
int lis(int len){
int max=1;
liss[0]=0;
for(int i=0;i<len;i++){
int index=find1(i,0, max-1);
if(index==0&&arr[liss[index]]>=arr[i]){
liss[index]=i;
continue;
}//增加这条语句主要是pre的影响,不需要路径的话,完全可以去掉。
if(index==max-1&&arr[liss[index]]<arr[i]){
liss[max++]=i;
pre[i]=liss[index];
continue;
}
liss[index]=i;
pre[i]=liss[index-1];
}

int k=liss[max-1],t=max-1;
while(pre[k]!=k){
res[t--]=arr[k];
k=pre[k];
}
res[t]=arr[k];
return max;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n+2;i++){
pre[i]=i;
}
for(int i=0;i<n;i++){
cin>>arr[i];
}
ll ans=lis(n);
printf("%lld\n",ans);
for(int i=0;i<ans;i++){
printf("%d ",res[i]);
}
printf("\n");
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: