shuoj1936-D序列—最長上升子序列
阿新 • • 發佈:2017-06-09
div 數據 出錯 spa ont 復雜 cap ear 輸出
Sample Output
233
思路::將A數組,B數組以A為第一keyword,B為第二keyword進行升序排序。然後將B倒置求B的最長上升子序列。 為了避免下標排序和寫比較函數。將A B 保存在pair裏先排序,然後再取出來存放大到 A 中。倒置,並求最長子序列。 在求最長上升子序列時,直接用dp的方法時間復雜度為 O(n^2),會超時,所以採用其它的方法求。 方法(1)::利用lower_bound 求上升子序列O(nlogn)
//lower_bound三個參數分別為要比較的起始點地址,終止點的地址+1(也就是左閉右開)。要比較的值(如果為d)。
//它的作用是返回一個地址。這個地址是在比較的範圍內>=d的最小的值的地址。
//舉個樣例,a[] = {0 , 1 ,2, 4, 5, 7 } p =lower_bound(a,a+6,3),p就為 4 的地址。假設p =lower_bound(a,a+6,4),p也為 4 的地址
方法(2)::利用二分法求上升子序列O(nlogn)
利用lower_bound要在數組中進行比較,當要比較的數較大時。無法將數存放在數組中。而利用二分法能解決這一問題,但代碼難度較大。
兩種方法的思路是一樣的。將數組A中子序列長度為 i 的最小值存放在數組S中。我們以3 2 4 6 5 7 3 為例進行演示行為遍歷。列為數組S,變化的地方已經標出來。有助於理解。
方法(1)代碼::
Description
已知兩個長度為N的數組A和B。下標從0標號至N-1。
如今定義一種D序列 (如果長度為L)。這樣的序列滿足下列條件:
1. 0 <= D[i] <= N-1
2. A[D[i]] < A[D[i+1]] (0 <= i < L-1)
3. B[D[i]] > B[D[i+1]] (0 <= i < L-1)
求滿足條件的D序列的最大長度。
(事實上這樣的序列叫做D序列的原因是:這道題是D題)
Input
多組數據,每組數據第一行是一個整數N。
(1 <= N <= 100000)
第二行中有N個正整數A[0]~A[N-1]。
第三行中有N個正整數B[0]~B[N-1]。
保證全部輸入整數在int範圍內。
Output
對每組數據。輸出一個整數。表示D序列的最大長度L。
Sample Input
3 1 1 2 3 2 1 3 1 2 3 3 2 1 4 1 3 2 4 3 1 2 4Sample Output
233
思路::將A數組,B數組以A為第一keyword,B為第二keyword進行升序排序。然後將B倒置求B的最長上升子序列。 為了避免下標排序和寫比較函數。將A B 保存在pair裏先排序,然後再取出來存放大到 A 中。倒置,並求最長子序列。 在求最長上升子序列時,直接用dp的方法時間復雜度為 O(n^2),會超時,所以採用其它的方法求。
在這裏a[ i ] > s[ j ]&&a[i]<=s[ j + 1 ]就應該把a[ i ]放在s[ j+1 ]的位置。
所以關鍵就是找出 j 就知道把a[ i ]放在哪了。
上面的兩種方法就是用來尋找 j的 。(在這裏lower_bound直接返回 j + 1 )
我們能夠發現s數組中的值必定是有序遞增的。這也是能夠利用二分法的一個必要條件。0 | 1 | 2 | 3 | 4 |
1 | 3 | |||
2 | 2 | |||
3 | 2 | 4 | ||
4 | 2 | 4 | 6 | |
5 | 2 | 4 | 5 | |
6 | 2 | 4 | 5 | 7 |
7 | 2 | 3 | 5 | 7 |
方法(1)代碼::
#include <bits/stdc++.h> using namespace std; int a[100005],b[100005]; int s[100005]; vector<pair<int,int> > T;//能夠用vector存,也能夠直接用數組 pair<int ,int> T[100005]; int main() { int n; while(~scanf("%d",&n)){ T.clear();//假設不初始或要出錯用數組就不須要了 for(int i = 0;i<n;i++)scanf("%d",&a[i]); for(int i = 0;i<n;i++)scanf("%d",&b[i]); //假設用數組應該為T[i] = {a[i],b[i]}; for(int i = 0;i<n;i++)T.push_back(make_pair(a[i],b[i])); //sort(T,T+n); std::sort(T.begin(),T.end());//排序 for(int i= 0;i<n;i++)a[i] = T[i].second;//把排序後的數組b取出來放到a中 reverse(a,a+n);//導致 int len = 1; s[1] = a[0];//<span style="font-family: Arial, Helvetica, sans-serif;">第一個元素首先放入 s[1]</span> for(int i = 1;i<n;i++){//dp的思想,逐個將a中元素增加s. int t = a[i]; if(t>s[len])s[++len] = a[i]; else{ int p = lower_bound(s+1,s+len+1,t)-s; s[p] = t; } } printf("%d\n",len); } return 0; }方法(2)代碼::
#include <bits/stdc++.h> using namespace std; int a[100005],b[100005]; int s[100005]; vector<pair<int,int> > T; int main() { int n; while(~scanf("%d",&n)) { T.clear(); for(int i = 0;i<n;i++)scanf("%d",&a[i]); for(int i = 0;i<n;i++)scanf("%d",&b[i]); for(int i = 0;i<n;i++)T.push_back(make_pair(a[i],b[i])); std::sort(T.begin(),T.end()); for(int i= 0;i<n;i++)a[i] = T[i].second; reverse(a,a+n); int len = 1;s[1] = a[0]; for(int i = 1;i<n;i++){ int t = a[i]; if(t>s[len]) s[++len] = a[i]; else{ int l = 1,r = len,mid; int ans = 0; while(l<=r)//這裏的二分法採用了左閉右閉的思路 { mid = (r+l)/2; if(s[mid]<t){ l = mid+1; ans=max(ans, mid);//ans即為思路中的j,j必定為s數組中小於t的最大的數 } else r = mid-1; } s[ans+1] = t; } } printf("%d\n",len); } return 0; }
shuoj1936-D序列—最長上升子序列