1. 程式人生 > >1134 最長上升子序列 (序列型 DP)

1134 最長上升子序列 (序列型 DP)

col 直接 般的 圖片 時間復雜度 方法 第一個 def 但是

技術分享圖片

思路: 由於一般的動態規劃時間復雜度是O(n^2)(哈哈哈哈 第一次用的就是這個!)用在這裏由於n最大為50000 所以會超時 到這裏我們可以用一個數組來動態維護這個最長上升的子序列,將你要輸入的子序列一個一個按升序存入數組 如果發現當前要存入的數字x比數組最後一個還要大 那麽直接存入數組,否則就將數組中按升序第一個大於x的數 用x替換掉(這裏的替換我們可以用二分搜索來進行) 由於二分搜索的時間復雜度是log(n) 所以總的時間復雜度為O(n log(n) ); 下面舉個例子

  例如 -6 4 -2 10 5 ,我們假設用p數組來存儲這個序列 那麽 p[0] = -6; 我們發現4 比-6大我們就直接將之存入 則 p[1] = 4,現在到 -2 我們用將數組總第一個大於-2的數用-2替換掉 則 p[1] = -2 ,現在p[]= ( -6,-2 ); 之後再用同樣的方法 最後的結果是p[] = ( -6 , -2 , 5 ), 這裏可以得知 (-6,-2,10) 的長度與前面的答案一致 但是因為5比10 小 所以如果後面還有數可以繼續拓展的話 很明顯 5 之後可以存的更多的數( 好像有點啰嗦!還是直接上代碼吧)

 1 /*
 2 //我們先來看一下第一次的超時代碼 
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstring>
 6  
 7 using namespace std;
 8 typedef long long LL;
 9 const LL maxn = 50005;
10 LL dp[maxn];
11 LL m[maxn];
12 LL n;
13 int main()
14 {
15     cin>>n;
16     for(int i=1;i<=n;i++)
17
{ 18 cin>>m[i]; 19 dp[i] = 1; 20 } 21 for(int i=2;i<=n;i++) 22 for(int j=i;j>=1;j--) 23 if(m[i]>m[j]) 24 dp[i] = max(dp[i],dp[j]+1); 25 LL ans = 0; 26 for(int i=1;i<=n;i++) 27 ans = max(ans,dp[i]); 28 cout<<ans<<endl;
29 return 0; 30 } 31 32 */ 33 34 //優化後的代碼 35 #include<iostream> 36 #include<algorithm> 37 #include<cstring> 38 39 using namespace std; 40 const int maxn = 50005; 41 int p[maxn],a[maxn]; 42 int n,len = 0; 43 int main() 44 { 45 cin>>n; 46 for(int i=0;i<n;i++) 47 cin>>a[i]; 48 p[0] = a[0]; 49 for(int i=1;i<n;i++) 50 { 51 if(a[i]>p[len])//當前數大於數組末尾的數 直接存入 52 p[++len] = a[i]; 53 else 54 { 55 int pos = upper_bound(p,p+len,a[i]) - p; 56 p[pos] = a[i];//將第一個大於當前數的目標用當前數替換掉 57 } 58 } 59 cout<<len + 1<<endl;//由於len是從0開始 所以答案要加一 60 return 0; 61 }

1134 最長上升子序列 (序列型 DP)