日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最长上升子序列(LIS)算法

發布時間:2024/10/6 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最长上升子序列(LIS)算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

LIS定義

LIS(Longest Increasing Subsequence)最長上升子序列?
一個數的序列bi,當b1 < b2 < … < bS的時候,我們稱這個序列是上升的。

對于給定的一個序列(a1, a2, …, aN),我們可以得到一些上升的子序列(ai1, ai2, …, aiK),

這里1 <= i1 < i2 < … < iK <= N。?
比如,對于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。

這些子序列中最長的長度是4,比如子序列(1, 3, 5, 8).?
你的任務,就是對于給定的序列,求出最長上升子序列的長度

兩種做法

O(N^2)做法:dp動態規劃

狀態設計:dp[i]代表以a[i]結尾的LIS的長度?
狀態轉移:dp[i]=max(dp[i], dp[j]+1) (0<=j< i, a[j]< a[i])?
邊界處理:dp[i]=1 (0<=j< n)?
時間復雜度:O(N^2)?
舉例: 對于序列(1, 7, 3, 5, 9, 4, 8),dp的變化過程如下

dp[i]初始值j=0j=1j=2j=3j=4j=5
dp[0]1??????
dp[1]12?????
dp[2]122????
dp[3]1223???
dp[4]12334??
dp[5]122333?
dp[6]1233444

求完dp數組后,取其中的最大值就是LIS的長度。【注意答案不是dp[n-1],這個樣例只是巧合】

#include<stdio.h> #include<iostream> #include<string.h> #include<queue> #include<cstdio> #include<string> #include<math.h> #include<algorithm> #include<map> #include<set> #include<stack> #define mod 998244353 #define INF 0x3f3f3f3f #define eps 1e-6 using namespace std; typedef long long ll; using namespace std; const int MAXX=10000+5;int a[MAXX],dp[MAXX]; // a數組為數據,dp[i]表示以a[i]結尾的最長遞增子序列長度 int n; int LIS(){int ans=1;for(int i=1; i<=n; i++)//枚舉子序列的終點{dp[i]=1;// 初始化為1,長度最短為自身for(int j=1; j<i; j++)//從頭向終點檢查每一個元素{if(a[i]>a[j]){dp[i]=max(dp[i],dp[j]+1); // 狀態轉移}}ans=max(ans,dp[i]); // 比較每一個dp[i],最大值為答案}return ans; } int main() {while(cin>>n){for(int i=1; i<=n; i++){cin>>a[i];}int ans=LIS();cout<<ans<<endl;}return 0; }

O(NlogN)做法:貪心+二分

a[i]表示第i個數據。?
dp[i]表示表示長度為i+1的LIS結尾元素的最小值。?
利用貪心的思想,對于一個上升子序列,顯然當前最后一個元素越小,越有利于添加新的元素,這樣LIS長度自然更長。?
因此,我們只需要維護dp數組,其表示的就是長度為i+1的LIS結尾元素的最小值,保證每一位都是最小值,

這樣子dp數組的長度就是LIS的長度。

dp數組具體維護過程同樣舉例講解更為清晰。?
同樣對于序列 a(1, 7, 3, 5, 9, 4, 8),dp的變化過程如下:

  • dp[0] = a[0] = 1,長度為1的LIS結尾元素的最小值自然沒得挑,就是第一個數。 (dp = {1})
  • 對于a[1]=7,a[1]>dp[0],因此直接添加到dp尾,dp[1]=a[1]。(dp = {1, 7})
  • 對于a[2]=3,dp[0]< a[2]< dp[1],因此a[2]替換dp[1],令dp[1]=a[2],因為長度為2的LIS,結尾元素自然是3好過于7,因為越小這樣有利于后續添加新元素。 (dp = {1, 3})
  • 對于a[3]=5,a[3]>dp[1],因此直接添加到dp尾,dp[2]=a[3]。 (dp = {1, 3, 5})
  • 對于a[4]=9,a[4]>dp[2],因此同樣直接添加到dp尾,dp[3]=a[9]。 (dp = {1, 3, 5, 9})
  • 對于a[5]=4,dp[1]< a[5]< dp[2],因此a[5]替換值為5的dp[2],因此長度為3的LIS,結尾元素為4會比5好,越小越好嘛。(dp = {1, 3, 4, 9})
  • 對于a[6]=8,dp[2]< a[6]< dp[3],同理a[6]替換值為9的dp[3],道理你懂。 (dp = {1, 3, 5, 8})

這樣子dp數組就維護完畢,所求LIS長度就是dp數組長度4。?
通過上述求解,可以發現dp數組是單調遞增的,因此對于每一個a[i],先判斷是否可以直接插入到dp數組尾部,

即比較其與dp數組的最大值即最后一位;如果不可以,則找出dp中第一個大于等于a[i]的位置,用a[i]替換之。?
這個過程可以利用二分查找,因此查找時間復雜度為O(logN),所以總的時間復雜度為O(N*logN)

#include <bits/stdc++.h> using namespace std; const int MAXX=100000+5; const int INF=INT_MAX;int a[MAXX],dp[MAXX]; // a數組為數據,dp[i]表示長度為i+1的LIS結尾元素的最小值int main() {int n;while(cin>>n){for(int i=0; i<n; i++){cin>>a[i];dp[i]=INF; // 初始化為無限大}int pos=0; // 記錄dp當前最后一位的下標dp[0]=a[0]; // dp[0]值顯然為a[0]for(int i=1; i<n; i++){if(a[i]>dp[pos]) // 若a[i]大于dp數組最大值,則直接添加dp[++pos] = a[i];else // 否則找到dp中第一個大于等于a[i]的位置,用a[i]替換之。dp[lower_bound(dp,dp+pos+1,a[i])-dp]=a[i]; // 二分查找}cout<<pos+1<<endl;}return 0; }

最長上升子序列

,即整個序列嚴格遞增

最長不下降子序列,也叫最長非遞減子序列

HDU5532

把每個數字減去對應位置的編號,然后求最長非遞減子序列長度即可

#include <cstdio> #include <cstring> #include <algorithm> #include<iostream> using namespace std; #define INF 0x3f3f3f3f typedef long long LL; int n; const int maxn=1e5+10; int a[maxn],dp[maxn]; int LIS(){int pos=0;dp[0]=a[0];for(int i=1;i<n;i++){if(a[i]>=dp[pos])//改變1:將大于該為大于等于dp[++pos]=a[i];else//改變2:查詢dp數組中第一個大于a[i]的位置,用a[i]代替dp[upper_bound(dp,dp+pos+1,a[i])-dp]=a[i];}return pos+1; } int main(){int T;scanf("%d",&T);int ca=1;while(T--){scanf("%d",&n);for(int i=0;i<n;i++){scanf("%d",&a[i]);a[i]-=i;dp[i]=INF;}int len=LIS();printf("Case #%d:\n",ca++);printf("%d\n",n-len);}return 0; }

?

總結

以上是生活随笔為你收集整理的最长上升子序列(LIS)算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。