N種方法妙講LIS算法


LIS算法經典匯總

假設存在一個序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出來它的LIS長度為5。
下面一步一步試着找出它。
我們定義一個序列B,然后令 i = 1 to 9 逐個考察這個序列。
此外,我們用一個變量Len來記錄現在最長算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是說當只有1一個數字2的時候,長度為1的LIS的最小末尾是2。這時Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是說長度為1的LIS的最小末尾是1,d[1]=2已經沒用了,很容易理解吧。這時Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是說長度為2的LIS的最小末尾是5,很容易理解吧。這時候B[1..2] = 1, 5,Len=2

再來,d[4] = 3,它正好加在1,5之間,放在1的位置顯然不合適,因為1小於3,長度為1的LIS最小末尾應該是1,這樣很容易推知,長度為2的LIS最小末尾是3,於是可以把5淘汰掉,這時候B[1..2] = 1, 3,Len = 2

繼續,d[5] = 6,它在3后面,因為B[2] = 3, 而6在3后面,於是很容易可以推知B[3] = 6, 這時B[1..3] = 1, 3, 6,還是很容易理解吧? Len = 3 了噢。

第6個, d[6] = 4,你看它在3和6之間,於是我們就可以把6替換掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len繼續等於3

第7個, d[7] = 8,它很大,比4大,嗯。於是B[4] = 8。Len變成4了

第8個, d[8] = 9,得到B[5] = 9,嗯。Len繼續增大,到5了。

最后一個, d[9] = 7,它在B[3] = 4和B[4] = 8之間,所以我們知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

於是我們知道了LIS的長度為5。

!!!!! 注意。這個1,3,4,7,9不是LIS,它只是存儲的對應長度LIS的最小末尾。有了這個末尾,我們就可以一個一個地插入數據。雖然最后一個d[9] = 7更新進去對於這組數據沒有什么意義,但是如果后面再出現兩個數字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的長度為6。

然后應該發現一件事情了:在B中插入數據是有序的,而且是進行替換而不需要挪動——也就是說,我們可以使用二分查找,將每一個數字的插入時間優化到O(logN)~~~~~於是算法的時間復雜度就降低到了O(NlogN)~!
借助例題 

hdu 1257 最少攔截系統

Problem Description
某國為了防御敵國的導彈襲擊,發展出一種導彈攔截系統.但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以后每一發炮彈都不能超過前一發的高度.某天,雷達捕捉到敵國的導彈來襲.由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈.
怎么辦呢?多搞幾套系統唄!你說說倒蠻容易,成本呢?成本是個大問題啊.所以俺就到這里來求救了,請幫助計算一下最少需要多少套攔截系統.
 

 

Input
輸入若干組數據.每組數據包括:導彈總個數(正整數),導彈依此飛來的高度(雷達給出的高度數據是不大於30000的正整數,用空格分隔)
 

 

Output
對應每組數據輸出攔截所有導彈最少要配備多少套這種導彈攔截系統.
 

 

Sample Input
8 389 207 155 300 299 170 158 65
 

 

Sample Output
2
經典LIS算法,與求最大上升子序列相似:
以數組 h[] 記錄攔截系統當前的攔截高度,先初始化為最大值 INF = 30000+10,
表示每一個新攔截系統都能攔截所有的導彈,然后遇到一個導彈就往前找看是否有已經使用了的系統能攔截,如果有,直接用;否則重新弄一個系統。最后再看用了幾個系統就好了。
 
第一個導彈 389 < h[1] ( h[1] = INF)被第一個系統攔截 h[1] = 389
第二個導彈 207 < h[1] 被第一個系統攔截 h[1] = 207
第三個導彈 155 < h[1] h[1] = 155
第四個導彈 300 > h[1] , 300 < h[2] ( h[2] = INF ) 所以新開發一個系統攔截第四個導彈, h[2] = 300
第五個導彈 299 > h[1] , 299 < h[2] 被第二個系統攔截 h[2] = 299
第六個導彈 170 > h[1] , 170 < h[2] h[2] = 170
第七個導彈 158 > h[1] , 158 < h[2] h[2] = 158
第八個導彈 65 < h[1] 被第一個系統攔截 h[1] = 65
 
所以最后使用了兩個系統就攔截了所有的導彈【遍歷 h[]數組從 1到 n 看有幾個 != INF 就說明使用了】
 
導彈高度:389 207 155 300 299 170 158 65
使用的攔截系統: 1 1 1 2 2 2 2 1
 求最長上升子序列:

      給定排好序的一堆數列中,求其的LIS長度。它的LIS長度就是它非上升子序列的個數。

此題可以用N種方法講解,下面一一為大家講解:

方法一:DP解法:

 1 #include<stdio.h>
 2 #define MAX(x,y) x>y?x:y
 3 int dp[10010],missile[10010];
 4 int main(){
 5     int N,max;
 6     while(~scanf("%d",&N)){max=0;
 7         for(int i=0;i<N;++i){dp[i]=1;
 8             scanf("%d",&missile[i]);
 9             for(int j=0;j<i;j++){
10                 if(missile[j]<missile[i])dp[i]=MAX(dp[j]+1,dp[i]);
11             }
12             max=MAX(max,dp[i]);
13         }
14         printf("%d\n",max);
15     }
16     return 0;
17 }

方法二:

二分法+貪心:

 

 1 #include<stdio.h>
 2 int missile[10010],Lis[10010];
 3 int r;
 4 void search(int x){
 5     int left=1,mid,right=r;
 6     while(left<=right){
 7         mid=(left+right)/2;//把 / 換成 >> 老是陷入死循環。。。。。 
 8         if(Lis[mid]<x)left=mid+1;
 9         else right=mid-1;
10     }
11     Lis[left]=x;
12 }
13 int main(){
14     int N;
15     while(~scanf("%d",&N)){r=1;
16     scanf("%d",&missile[0]);Lis[r]=missile[0];
17         for(int i=1;i<N;i++){
18             scanf("%d",&missile[i]);
19             if(missile[i]>Lis[r])Lis[++r]=missile[i];
20             else search(missile[i]);
21         }
22         printf("%d\n",r);
23     }
24     return 0;
25 }

方法三:

STL+二分+貪心:

 

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 int Lis[10010]; 
 5 int main(){
 6     int N,r,x;
 7     while(~scanf("%d",&N)){r=0;
 8         scanf("%d",&x);
 9         Lis[r]=x;
10         for(int i=1;i<N;++i){
11             scanf("%d",&x);
12             if(x>Lis[r])Lis[++r]=x;
13             else *lower_bound(Lis,Lis+r,x)=x; 
14         }
15         printf("%d\n",r+1);
16     }
17     return 0;
18 }

 

方法四:

vector+二分+貪心:

 

 1 #include<stdio.h>
 2 #include<vector>
 3 #include<algorithm>
 4 using namespace std;
 5 int main(){
 6     int N,x;
 7     while(~scanf("%d",&N)){
 8         vector<int>Lis;vector<int>::iterator iter;
 9         while(N--){
10             scanf("%d",&x);
11             iter=lower_bound(Lis.begin(),Lis.end(),x);
12             if(iter==Lis.end())Lis.push_back(x);
13             else *iter=x;
14         }
15         printf("%d\n",Lis.size());
16     }
17     return 0;
18 }

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM