給出一個序列a, 求a的最長上升子序列c++
輸入格式:git
第一行是一個數n,算法
接下來一行,每行爲n個數數組
輸出格式:函數
一個數,即最長上升子序列的長度優化
樸素版的lis是O(N ^ 2)的作法,這裏就不在給出;當數據大時很容易被卡,經過二分優化 + 貪心能夠優化成爲O(NlogN),首先介紹兩個函數:spa
lower_bound( )和upper_bound( )是利用二分查找的方法在一個有序的數組中進行查找的。code
當數組是從小到大時,orm
lower_bound( begin,end,num):表示從數組的begin位置到end-1位置二分查找第一個大於或等於num的數字,找到返回該數字的地址,不存在則返回end。經過返回的地址減去起始地址begin,找到數字在數組中的下標。blog
upper_bound( begin,end,num):表示從數組的begin位置到end-1位置二分查找第一個大於num的數字,找到返回該數字的地址,不存在則返回end。經過返回的地址減去起始地址begin,找到數字在數組中的下標。
當數組是從大到小時,咱們須要重載lower_bound()和upper_bound();
struct cmp{bool operator()(int a,int b){return a>b;}};
lower_bound( begin,end,num,cmp() ):從數組的begin位置到end-1位置二分查找第一個小於或等於num的數字,找到返回該數字的地址,不存在則返回end。經過返回的地址減去起始地址begin,獲得找到數字在數組中的下標。
upper_bound( begin,end,num,cmp() ):從數組的begin位置到end-1位置二分查找第一個小於num的數字,找到返回該數字的地址,不存在則返回end。經過返回的地址減去起始地址begin,獲得找到數字在數組中的下標。
b[i] 表示長度爲i的上升子序列的最後一個數的最小值,若是a[i] > b[i] 則顯然子序列的長度加1;不然找到找到第一個比它大的值將其替換,最終能夠找到lis的長度;
/*大佬們能夠手寫二分...*/
#include <bits/stdc++.h> using namespace std; #define ll long long #define INF 0x3f3f3f3f #define MAXN 1000010 #define MAXM 5010 inline int read() { int x = 0,ff = 1;char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * ff; } int n,ans = 1,a[MAXN],b[MAXN]; int main() { n = read(); for(int i = 1;i <= n;++i) a[i] = read(); b[1] = a[1]; for(int i = 2;i <= n;++i) { if(b[ans] < a[i]) b[++ans] = a[i]; else b[lower_bound(b + 1,b + ans + 1,a[i]) - b] = a[i]; } printf("%d\n",ans); return 0; }
手寫二分以下:
#include <bits/stdc++.h> using namespace std; #define MAXX 301000 int n; int a[MAXX], f[MAXX]; int top = 0; void find(int k) { int left = 1, right = top; while(left + 1 < right) { int mid = (left + right) / 2; if(f[mid] >= k) { right = mid; } else if(f[mid] < k) left = mid; } // cout << left << ' ' << right << ' ' << top << endl; if(f[left] > k) f[left] = k; else if(f[right] > k && f[left] <= k) f[right] = k; } int main() { memset(f, 0, sizeof(f)); scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); if(a[i] > f[top]) { f[++top] = a[i]; } else if(a[i] < f[top]) { find(a[i]); } } printf("%d", top); return 0; }
描述 Description
有N個整數,輸出這N個整數的最長上升序列、最長降低序列、最長不上升序列和最長不降低序列。
輸入格式 Input Format
第一行,僅有一個數N。 N<=700000
第二行,有N個整數。 -10^9<=每一個數<=10^9
輸出格式 Output Format
第一行,輸出最長上升序列長度。
第二行,輸出最長降低序列長度。
第三行,輸出最長不上升序列長度。
第四行,輸出最長不降低序列長度。
這豈不是很顯然:
#include <bits/stdc++.h> using namespace std; #define ll long long #define INF 0x3f3f3f3f #define MAXN 1000010 #define MAXM 5010 inline int read() { int x = 0,ff = 1;char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * ff; } int n,ans1,ans2,ans3,ans4,a[MAXN]; int b1[MAXN],b2[MAXN],b3[MAXN],b4[MAXN]; struct cmp{bool operator()(int a,int b){return a>b;}}; int main() { n = read(); for(int i = 1;i <= n;++i) a[i] = read(); ans1 = ans2 = ans3 = ans4 = 1; b1[1] = b2[1] = b3[1] = b4[1] = a[1]; for(int i = 2;i <= n;++i) { if(a[i] > b1[ans1]) b1[++ans1] = a[i]; else b1[lower_bound(b1 + 1,b1 + ans1 + 1,a[i]) - b1] = a[i]; if(a[i] < b2[ans2]) b2[++ans2] = a[i]; else b2[lower_bound(b2 + 1,b2 + ans2 + 1,a[i],cmp()) - b2] = a[i]; if(a[i] <= b3[ans3]) b3[++ans3] = a[i]; else b3[upper_bound(b3 + 1,b3 + ans3 + 1,a[i],cmp()) - b3] = a[i]; if(a[i] >= b4[ans4]) b4[++ans4] = a[i]; else b4[upper_bound(b4 + 1,b4 + ans4 + 1,a[i]) - b4] = a[i]; } printf("%d\n%d\n%d\n%d\n",ans1,ans2,ans3,ans4); return 0; }
補充:這篇博客都發好長時間了, 在補充一些這樣的題吧。
給出1-n的兩個排列P1和P2,求它們的最長公共子序列。
第一行是一個數n,
接下來兩行,每行爲n個數,爲天然數1-n的一個排列。
一個數,即最長公共子序列的長度
emmmmm, 看到這個咱們應該很容易想到一個$O(n^2)$的作法, 設$f[i][j]$表示a序列匹配到i, b序列匹配到j的最大公共長度, 則轉移顯然有
$$f[i][j] =\begin{cases}\max(f[i - 1][j], f[i][j - 1]) & a[i] \not= a[j] \\f[i - 1][j - 1] + 1 &a[i] = a[j]\\\end{cases} $$
噹噹前數字不相等時, 考慮繼承前面的狀態, 因此取max, 而相等時, 就能夠從前面的狀態更新過來。 但這道題$O(n^2)$的複雜度顯然是過不去。 咱們能夠想到排列的性質, 一個序列1~n,不重不漏的每一個數字都有, 咱們不妨開一個數組表示每一個數在序列a中出現的位置,這樣咱們遍歷b中的每個數字, 知道其在a中對應的值, 若是知足單調上升, 就證實這兩個子序列能夠匹配, 這樣用轉化成了咱們著名的lis算法, 在$O(nlogn)$ 的複雜度中就能夠求出
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN = 1e5 + 100; const int MAXM = 5e3 + 10; const double eps = 1e-5; template < typename T > inline void read(T &x) { x = 0; T ff = 1, ch = getchar(); while (!isdigit(ch)) { if (ch == '-') ff = -1; ch = getchar(); } while (isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } x *= ff; } template < typename T > inline void write(T x) { if (x == 0) { putchar('0'); return ; } if (x < 0) putchar('-'), x = -x; static T tot = 0, ch[30]; while (x) { ch[++tot] = x % 10 + '0'; x /= 10; } while (tot) putchar(ch[tot--]); } int n, ans, a[MAXN], b[MAXN], f[MAXN], vis[MAXN]; int main() { read(n); for (register int i = 1; i <= n; ++i) read(a[i]), vis[a[i]] = i; for (register int i = 1; i <= n; ++i) read(b[i]); for (register int i = 1; i <= n; ++i) { if (vis[b[i]] > f[ans]) f[++ans] = vis[b[i]]; else { int k = lower_bound(f + 1, f + ans + 1, vis[b[i]]) - f; f[k] = vis[b[i]]; } } write(ans); return 0; }
這是一次模擬賽的題, 也是我A掉的一道題, 又是數字序列匹配的一道問題, 一樣是使兩個序列相同。 咱們首先或許會想到,匹配a,b的最長公共子序列, 而後, 你就跑不過樣例, 由於你操做的只有a數組, 你能夠吧a子序列中不合法的數丟到隊頭或隊尾,但b是不能夠移動的。 那就致使b子序列中間的數字沒法匹配。 其實這樣咱們的思路就很清晰了, 既然b動不了, 就不動它唄。 用a的子序列去匹配b的字串。 因爲這兩個序列依然是n的排列, 因此咱們能夠$O(n)$的求出。
#include <bits/stdc++.h> using namespace std; typedef long long ll; //const int INF = 0x3f3f3f3f; const int MAXN = 2e5 + 100; //const int MAXM = 2e3 + 10; //const double eps = 1e-5; template < typename T > inline void read(T &x) { x = 0; T ff = 1, ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } x *= ff; } template < typename T > inline void write(T x) { if(x == 0) { putchar('0'); return ; } if(x < 0) putchar('-'), x = -x; static T ch[30], tot = 0; while(x) { ch[++tot] = x % 10 + '0'; x /= 10; } while(tot) putchar(ch[tot--]); } int n, ans, a[MAXN], b[MAXN], vis[MAXN]; int main() { freopen("sequence.in", "r", stdin); freopen("sequence.out", "w", stdout); read(n); for (register int i = 1; i <= n; ++i) { read(a[i]); vis[a[i]] = i; } a[n + 1] = 0; vis[0] = -1; for (register int i = 1; i <= n; ++i) read(b[i]); int i = 1; while (i <= n) { int cnt = 1; while (++i <= n + 1) { if (vis[b[i]] > vis[b[i - 1]]) ++cnt; else break; } ans = max(ans, cnt); } write(n - ans); return 0; }