51nod1134最长递增子序列(dp)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134

这里说下,最长上升子序列和最长不降子序列几乎一样,只是判断=的时候注意一下。另外最长不降子序列经常反过来考,有几个最长不降,而不是求它的长度。(经典例题导弹拦截系统)

分析和思路:

 我们维护当前最长的长度len,用vis[j]存储长度为j的所有子序列中最小的末尾数值,那么对于当前数据a[i],如果数组vis中存在比其大的元素我们用a[i]替换掉vis中第一个比a[i]大的数,若不存在,那么我们将a[i]加入vis末尾,此时数组长度+1,如此我们便维护了数组vis的性质,最终得到的len就是答案了.因为数组vis再加入时就保持了递增的性质,所以在查找时可以用二分查找函数lower_bound把时间复杂度降到n*logn。

当然这题不用二分函数也可以过,其实我反而觉得不用函数更好(当然再不超时的前提下),自己一步一步的慢慢实现这个过程能极大的帮助理解这个方法(算法)的本质,体会到它的巧妙(当初仔细想了几遍,想通了巧妙之后发现它是如此有趣^^),过段时间后也不会轻易忘记 。

这个题是要求严格递增子序列(ps:这类题<=或<的地方要区分清楚理解清楚)

 1 #include <iostream>
 2 using namespace std;
 3 const int maxn=1e6+5;
 4 int a[maxn],vis[maxn];
 5 int main()
 6 {
 7       int n;
 8       cin>>n;
 9       for(int i=1;i<=n;i++) cin>>a[i];
10       for(int i=0;i<=n;i++) vis[i]=-1e9-1;
11       
12       vis[1]=a[1];int len=1;
13       for(int i=2;i<=n;i++)
14       {
15           int f=0;
16           for(int j=1;j<=len;j++)//<len就行不是i(len即为该递增子序列) 
17           {
18               if(a[i]<=vis[j])//区别就在这,有=,把相等的算进来如此就在递增序列中舍弃了(严格最长递增子序列,如1,2,3,4)
19               {                 //如果是a[i]<vis[j],就把相等的算在递增序列中了(非严格最长递增子序列,如1,2,2,2)
20                   f=1;
21                   vis[j]=a[i];
22                   break;
23               }
24           }
25           if(!f) vis[++len]=a[i];//递增序列中都比a[i]<=,所以序列长度可+1
26       }
27       cout<<len<<endl;
28       
29       return 0;
30 } 

还有一道极类似题,hdu最少拦截系统,http://acm.hdu.edu.cn/showproblem.php?pid=1257

分析和思路:

其实就是把lis反过来理解,代码都不用变!只不过这个思维转换的过程不好想,我也是看了别人说的之后才转换过来想通。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int maxn=1e6+5; 
 6 int a[maxn],b[maxn],vis[maxn];
 7 int main()
 8 {
 9     ios::sync_with_stdio(false);cin.tie(0);
10      
11     int n;
12     while(cin>>n)
13     {
14          for(int i=1;i<=n;i++) cin>>a[i];
15          for(int i=0;i<=n;i++) vis[i]=-1e9-1;
16      
17          vis[1]=a[1];int len=1;
18          for(int i=2;i<=n;i++)
19          {
20              int f=0;
21              for(int j=1;j<=len;j++)
22              {                      //=重要!把相等时(不用再开一套系统)也要装进去(代表非严格递降,即不高于)
23                  if(a[i]<=vis[j])//如果<,则是严格递降,是高于 
24                  {                  //与lis意义不同,高度降低代表当前这套的最大高度 
25                      f=1;
26                      vis[j]=a[i];
27                      break;
28                  }
29              }
30              if(!f) vis[++len]=a[i];//与lis意义不同,代表增加一套系统(lis是存的那一条最长递增列) 
31          }
32          cout<<len<<endl;
33          memset(vis,0,sizeof(vis));
34     }
35      
36     return 0;
37 } 

洛谷上述两个,https://www.luogu.org/problemnew/show/P1020

反着正着都考了,同时更新为二分查找做法

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <vector>
 5 #include <algorithm>
 6 #define sc(x) scanf("%d",&x)
 7 #define pr(x) printf("%d",x)
 8 #define ppp putchar('
')
 9 using namespace std;
10 const int maxn=1e6+5; 
11 int a[maxn],vis[maxn],viss[maxn];
12 int main()
13 {
14     int n=1;
15     while(sc(a[n])!=EOF) n++;
16     n--;
17     for(int i=0;i<=n;i++) vis[i]=-1e9-1;
18     
19     int len1=1; 
20     vis[1]=a[1];
21     for(int i=2;i<=n;i++)
22     {
23         /*for(int j=1;j<=len1;j++)
24         {                      
25             if(a[i]<=vis[j]) 
26             {
27                 f1=1;
28                 vis[j]=a[i];
29                 break;
30             }
31         }*/
32  
33         int f1=lower_bound(vis+1,vis+1+len1,a[i])-(vis+1)+1;
34         if(f1<=len1) vis[f1]=a[i];
35         else vis[++len1]=a[i]; 
36     }
37     
38     reverse(a+1,a+1+n);
39     int len2=1; 
40     viss[1]=a[1];
41     for(int i=2;i<=n;i++)
42     {
43         /*for(int j=1;j<=len2;j++)
44         {                      
45             if(a[i]<viss[j]) 
46             {
47                 f2=1;
48                 viss[j]=a[i];
49                 break;
50             }
51         }*/
52         int f2=upper_bound(viss+1,viss+1+len2,a[i])-(viss+1)+1;
53         if(f2<=len2) viss[f2]=a[i];
54         else viss[++len2]=a[i]; 
55     }
56     pr(len2);ppp;
57     pr(len1);ppp;
58     
59     memset(vis,0,sizeof(vis));
60     memset(viss,0,sizeof(viss));
61     
62     return 0;
63 }

原文地址:https://www.cnblogs.com/redblackk/p/9533435.html