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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

最长上升子序列(LIS) nlogn解法

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

文章目錄

      • 經典DP解法O(n^2)
      • dp+二分法(O(nlogn))

最長上升子序列LIS:Longest increasing subsequence

題目鏈接:Leetcode300. 最長遞增子序列

經典DP解法O(n^2)

數據范圍:1000.
時間復雜度O(n2^22)
空間復雜度O(n)
分析
轉移方程

j= 0,…,i-1
if(nums[j]<nums[i])
dp[i]=max(dp[i],dp[j]+1);

ac代碼

class Solution { public:int lengthOfLIS(vector<int>& nums) {int n=nums.size();vector<int> dp(n,1);//賦值為1int ans=0;for(int i=0;i<n;i++){for(int j=0;j<i;j++)if(nums[j]<nums[i])dp[i]=max(dp[i],dp[j]+1);ans=max(dp[i],ans);}return ans;} };

下面是進行優化

dp+二分法(O(nlogn))

題目Acwing896. 最長上升子序列 II

數據范圍10510^5105,需要nlogn的算法。

給定一個長度為N的數列,求數值嚴格單調遞增的子序列的長度最長是多少。

輸入格式
第一行包含整數N。

第二行包含N個整數,表示完整序列。

輸出格式
輸出一個整數,表示最大長度。

數據范圍
1≤N≤100000,
?109≤數列中的數≤109
輸入樣例:
7
3 1 2 1 8 5 6
輸出樣例:
4
時間復雜度O(nlogn)
空間復雜度O(n)
分析
維護一個數組vec,vec[i]表示 長度為 i+1的LIS的 最小的終點

長度相同的LIS只存終點的最小值。

隨著LIS長度的增加,其結尾的值(最小的那個)一定是單調遞增的,比如:長度為4的LIS的終點值< 長度為5的LIS的終點值。

該策略是讓小元素盡可能更新在前面,替換掉前面的大元素,這是因為 前面的數越小,LIS的長度才可能越長。
舉例(1,5,2,3,4,6)
更新vec如下
第一個1來,vec[0]=1 此時 vec={1}
第二個5來,因為5> 1,vec 拓展,vec[1]=5,此時 vec[ ] ={1,5}
第三個2 來,而2<5, 替換之,vec[1]=2;此時 vec[ ]={1,2}
第四個3來,因為3>2,vec拓展,vec[2]=3;此時 vec[ ]={1,2,3}
第五個4來,因為4>3,vec拓展,vec[3]=4;此時 vec[ ]={1,2,3,4}
第六個6來,因為6>4,vec拓展,vec[4]=6;此時 vec[ ] ={1,2,3,4,6}
最后返回vec數組的長度 LIS=5

lower_bound 函數,查找nums[i] ,返回大于等于nums[i]的第一個位置
lower_bound()函數返回的是一個迭代器(可以簡單地理解成地址),我們需要減去初始迭代器vec.begin()(初始地址),得到的才是下標。
即公式:地址-地址=下標

int p=lower_bound(vec.begin(),vec.end(),nums[i])-vec.begin();//二分查找,

比如vec[ ]= {1,5} 現在新來元素為2,上述lower_bound代碼返回下標p=1,這里就是≥2的第一個位置,即5所在的位置,這時候我們怎么處理呢?沒錯,就是替換掉5,使用語句vec[p]= 2, 我們維護的偽LIS數組就變成了vec[ ]= {1,2} ,LIS長度沒變,只是在保證單調性條件下,使該長度的上升子序列結尾的元素盡可能的小。

class Solution { public:int lengthOfLIS(vector<int>& nums) {int n=nums.size();vector<int> vec;for(int i=0;i<n;i++){int p=lower_bound(vec.begin(),vec.end(),nums[i])-vec.begin();//二分查找,返回大于等于nums[i]的第一個位置if(p==vec.size())//添加進來vec.push_back(nums[i]);elsevec[p]=nums[i];//替換掉}return vec.size();//返回所維護數組的大小} };

如果是最長不降子序列
采用upper_bound()函數,返回第一個大于x的位置這樣的話,遇到相等的元素,則一直會添加到數組中來
比如 (000011111)這樣的二進制序列
對應下面的題目

題目如下
Leetcode 926 將字符串翻轉到單調遞增
如果一個由 ‘0’ 和 ‘1’ 組成的字符串,是以一些 ‘0’(可能沒有 ‘0’)后面跟著一些 ‘1’(也可能沒有 ‘1’)的形式組成的,那么該字符串是單調遞增的。

我們給出一個由字符 ‘0’ 和 ‘1’ 組成的字符串 S,我們可以將任何 ‘0’ 翻轉為 ‘1’ 或者將 ‘1’ 翻轉為 ‘0’。

返回使 S 單調遞增的最小翻轉次數。

示例 1:
輸入:“00110”
輸出:1
解釋:我們翻轉最后一位得到 00111.
示例 2:
輸入:“010110”
輸出:2
解釋:我們翻轉得到 011111,或者是 000111。
示例 3:
輸入:“00011000”
輸出:2
解釋:我們翻轉得到 00000000。

提示:
1 <= S.length <= 20000
S 中只包含字符 ‘0’ 和 ‘1’

時間復雜度O(nlogn)
空間復雜度O(n)
當然這題最快的是O(n)的算法

ac代碼

class Solution { public:int minFlipsMonoIncr(string S) {int len=S.size();vector<char> vec;for(int i=0;i<len;i++){int p = upper_bound(vec.begin(),vec.end(),S[i])-vec.begin();if(vec.size()==p) vec.push_back(S[i]);elsevec[p]=S[i];}return len-vec.size();}};

后面再補充另一種思維

題目Acwing896. 最長上升子序列 II該題目下的ac代碼1

/*長度相同的LIS只存終點的最小值。隨著LIS長度的增加,其結尾的值一定是單調遞增的比如:長度為4的LIS的終點值< 長度為5的LIS的終點值*//*二分出來小于某個數的最大的數 */#include<bits/stdc++.h> using namespace std;const int N=100010;int a[N]; //原序列 int q[N]; //存不同長度的上升子序列結尾的最小值。 int n; int main(){cin>>n;for(int i=0;i<n;i++) cin>>a[i];int len=0;//存的上升子序列的最大長度q[0]=-2e9;//哨兵//二分:[0,len]中小于某個數的最大的數for(int i=0;i<n;i++){int l=0,r = len;while(l<r){int mid = l+r+1 >>1;if(q[mid]<a[i]) l = mid ;else r = mid -1;}len =max(len , r+1);q[r+1]=a[i]; //后面添上一個} cout<<len<<endl;}

acwing這道題ac代碼2

#include<bits/stdc++.h> using namespace std;const int N=100010; int a[N];int LIS(int a[],int n){vector<int> vec;for(int i=0;i<n;i++){int p= lower_bound(vec.begin(),vec.end(),a[i])-vec.begin(); //找到第一個大于等于a[i]的位置if(p==vec.size()) vec.push_back(a[i]); //如果長度相等,并且新來的元素大于末尾,就替換掉else vec[p]=a[i]; }return vec.size();}int main(){int n;cin>>n;for(int i=0;i<n;i++) cin>>a[i];cout<<LIS(a,n)<<endl;}

總結

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

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