[dp]最長單調遞增子序列
阿新 • • 發佈:2017-05-13
bsp 存在 ont for printf iss 需要 hellip 註意
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(inti,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 intindex=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]最長單調遞增子序列