1. 程式人生 > >[dp]最長單調遞增子序列

[dp]最長單調遞增子序列

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(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]最長單調遞增子序列