首页 > 代码库 > [dp]最长单调递增子序列

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

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

解题关键:

如果将子序列按照长度由短到长排列,将他们的最大元素放在一起,形成新序列B{b1,b2,……bj},则序列B满足b1 < b2 < …… <bj。这个关系比较容易说明,假设bxy表示序列A中长度为x的递增序列中的第y个元素,显然,如果在序列B中存在元素bmm > bnn,且m < n则说明子序列Bn的最大元素小于Bm的最大元素,因为序列是严格递增的,所以在递增序列Bn中存在元素bnm < bnn,且从bn0到bnm形成了一个新的长度为m的递增序列,因为bmm > bnn,所以bmm > bnm,这就说明在序列B中还存在一个长度为m,最大元素为bnm < bmm的递增子序列,这与序列的定义,bmm是所有长度为m的递增序列中第m个元素最小的序列不符,所以序列B中的各元素严格递增。

 

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

1、只求数量模板

 (1)liss为索引模板

 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 using namespace std; 7 typedef long long ll; 8 int arr[50002],liss[50002]; 9 int find1(int i,int l,int r){10     int mid;11     while(l<r){12         mid=(l+r)>>1;13         if(arr[liss[mid]]>arr[i]) r=mid;14         else l=mid+1;15     }16     return r;17 }18 int lis(int len){19     int max=1;20     liss[0]=0;21     for(int i=0;i<len;i++){22         int index=find1(i,0, max-1);23         if(index==max-1&&arr[liss[index]]<arr[i]){24             liss[max++]=i;25             continue;26         }27         liss[index]=i;28     }29     return max;30 }31 int main(){32     int n;33     cin>>n;34     for(int i=0;i<n;i++){35         cin>>arr[i];36     }37     ll ans=lis(n);38     printf("%lld\n",ans);39     return 0;40 }

 (2)liss为值模板

 

 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 using namespace std; 7 typedef long long ll; 8 int arr[50002],liss[50002]; 9 int find1(int i,int l,int r){10     int mid;11     while(l<r){12         mid=(l+r)>>1;13         if(liss[mid]>arr[i]) r=mid;14         else l=mid+1;15     }16     return r;17 }18 int lis(int len){19     int max=1;20     liss[0]=0;21     for(int i=0;i<len;i++){22         int index=find1(i,0, max-1);23         if(index==max-1&&liss[index]<arr[i]){24             liss[max++]=arr[i];25             continue;26         }27         liss[index]=arr[i];28     }29     return max;30 }31 int main(){32     int n;33     cin>>n;34     for(int i=0;i<n;i++){35         cin>>arr[i];36     }37     ll ans=lis(n);38     printf("%lld\n",ans);39     return 0;40 }

 

 

2、带路径模板

 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 using namespace std; 7 typedef long long ll; 8 int arr[50002],liss[50002],pre[50002],res[50002]; 9 int find1(int i,int l,int r){10     int mid;11     while(l<r){12         mid=(l+r)>>1;13         if(arr[liss[mid]]>arr[i]) r=mid;14         else l=mid+1;15     }16     return r;17 }18 int lis(int len){19     int max=1;20     liss[0]=0;21     for(int i=0;i<len;i++){22         int index=find1(i,0, max-1);23         if(index==0&&arr[liss[index]]>=arr[i]){24             liss[index]=i;25             continue;26         }//增加这条语句主要是pre的影响,不需要路径的话,完全可以去掉。27         if(index==max-1&&arr[liss[index]]<arr[i]){28             liss[max++]=i;29             pre[i]=liss[index];30             continue;31         }32         liss[index]=i;33         pre[i]=liss[index-1];34     }35     36     int k=liss[max-1],t=max-1;37     while(pre[k]!=k){38         res[t--]=arr[k];39         k=pre[k];40     }41     res[t]=arr[k];42     return max;43 }44 int main(){45     int n;46     cin>>n;47     for(int i=0;i<n+2;i++){48         pre[i]=i;49     }50     for(int i=0;i<n;i++){51         cin>>arr[i];52     }53     ll ans=lis(n);54     printf("%lld\n",ans);55     for(int i=0;i<ans;i++){56         printf("%d ",res[i]);57     }58     printf("\n");59 }

 

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