最近因為要出去比賽,所以回顧了以往的題目,於是無聊的把遞增子串行弄出來,自己嘗試著推到了下o(nlogn)的演算法,發現挺容易的,於是修改了下以往的部落格,然後寫了些自己的想法。
首先要懂nlogn演算法的前提是要明白o(n^2)演算法,最初始的dp十分暴力列舉每個結尾計算以i為結尾的最長遞增序列長度,dp方程:dp[i] = max。我們分析下這個方程,狀態轉移返程可以轉化為如下:
dp[i]=max+1,其實就是要找到[1,i]區間上的最大值,但是這個最大值j要滿足a[i]>a[j],那麼是否可以考慮將之前的a[i]存入乙個佇列,這個佇列是按照遞增的順序,對於佇列中某個位置p,那麼q[p]就是到目前i位置為止長度為p的遞增子串行結尾點的值。那麼就可以根據二分,查詢到滿足a[i] > q[t]的乙個上限,那麼每次查詢最大值的時間就從o(n)變成了o(logn),於是演算法總複雜度o(nlogn)。很美的時間複雜度。
以poj 2533為例
#include#include#include#include#include#include#include#include#include#include#include#define b(x) (1<<(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned ui;
const int oo = 0x3f3f3f3f;
//const ll oo = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;
#define lson rt<<1
#define rson rt<<1|1
void cmax(int& a, int b)
void cmin(int& a, int b)
void cmax(ll& a, ll b)
void cmin(ll& a, ll b)
void cmax(double& a, double b)
void cmin(double& a, double b)
void add(int& a, int b, int mod)
void add(ll& a, ll b, ll mod)
const ll mod = 1000000007;
const int maxn = 1100;
int q[maxn], a[maxn];
int main()
q[r] = a[i];
cmax(ans, r);
} printf("%d\n", ans);
} return 0;
}
o(nlogn)演算法時效性,所謂時效性就是無法馬上根性佇列裡面的情況,當然之前的例子是無間隔的遞曾子串行,如果變成間隔為t,那麼就每次找到的最優解無法馬上入佇列。請看一下例子:
例子 hdu 4521
題目意思大概就是說找出間隔至少為d的子串行的最長長度。
這題如果用o(n^2)演算法很容易處理,因為只要在列舉j的時候判斷下是否滿足間隔小於d就好了。但是這題資料比較大恰恰用n^2演算法會超時,所以考慮用(nlogn)演算法。
要實現間隔為d的確有點頭疼(這裡體現時效性不好),那麼仔細思考會發現其實每次佇列都是二分完就馬上跟新,但是為了完成間隔d的任務這裡就要稍微改變下。那麼我們這樣考慮,我們然某個二分完的數在d次二分之後再更新進入佇列,那麼這樣通過時間的岔開就完成了間隔d的任務。
這裡例子我們看出nlogn演算法時效性不好,但是還是可以通過一些技巧來補救,總的來說o(nlogn)演算法還是很美的。
#include#include#include#include#include#include#include#include#include#include#include#define b(x) (1<<(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned ui;
const int oo = 0x3f3f3f3f;
//const ll oo = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;
#define lson rt<<1
#define rson rt<<1|1
void cmax(int& a, int b)
void cmin(int& a, int b)
void cmax(ll& a, ll b)
void cmin(ll& a, ll b)
void cmax(double& a, double b)
void cmin(double& a, double b)
void add(int& a, int b, int mod)
void add(ll& a, ll b, ll mod)
const ll mod = 1000000007;
const int maxn = 110000;
int q[maxn], a[maxn], dp[maxn];
int main()
dp[i] = r;
if (i - d > 0) cmin(q[dp[i - d]], a[i - d]);
} int ans = 0;
for (int i = 1; i <= n; i++)
cmax(ans, dp[i]);
printf("%d\n", ans);
} return 0;
}
最長遞增子串行 nlogn
include using namespace std 2 1 5 3 6 4 8 9 7 上課講了這個,回去之後就用 實現以下 這個演算法的目標是 找到乙個最長的遞增子串行 o nlogn 的複雜度 思路是遇到小的就留下,更新的路程中 插入到這個序列當中 一開始看那個麻瓜 blog 理解錯了 然後...
最長遞增子串行nlogn的做法
費了好大勁寫完的 用線段樹維護的 nlogn的做法 再看了一下 大神們寫的 nlogn 額差的好遠 我寫的又多又慢 大神們寫的又少又快 時間 空間 量 哪個都趕不上大佬們的 這是我寫的 include include include include include include using nam...
dp最長遞增子串行的nlogn演算法實現
n2的演算法很簡單,這裡是利用另外乙個陣列d,d i 存的是長度為i的最末尾元素。因為d i 是有序的,可以用二分查詢優化,複雜度降低為nlogn,如下,注意二分查詢的邊界 include include include using namespace std const int n 41000 i...