最长上升子序列(Longest Increasing Subsequence)
在一个给定序列中{a[i]},按顺序挑选出若干项(不一定连续),组成一个新序列,成为“子序列”.如果子序列中每相邻的两项中后一项比前一项大则称这个子序列为“上升子序列”.在这些上升子序列中长度最长的即为“最长上升子序列”.
O(n^2)做法:
dp[i]为以第i项结尾的最长上升子序列的长度.
有如下状态转移方程:
dp[i] = max{dp[j] + 1 | a[j] < a[i]}
O(nlogn)做法:
维护一个数组w[],w[i]为长度为i的最长上升子序列的最后一项最小是多少.
不难发现w[]是单调的.
证明:
采用反证法
存在x < y
而w[x] > w[y]
设w[x] = a,w[y] = b
因为dp[i] = max{dp[j] + 1 | a[j] < a[i]}
存在一个c < b < a
以c结尾的最长上升子序列长度与以a结尾的最长上升子序列长度相等
所以a不是长度为x的最长上升子序列的最后一个元素的最小值
矛盾
因此w[]是单调的
可以利用w[]通过二分查找确定答案.
code:
const int M = ~0u >> 1;
int w[maxn];
int LIS(int a[],int n)
{
int ans = 1,l,r,mid,ret;
for(int i = 1; i < n; ++i) w[i] = M;
for(int i = 0; i < n; ++i){
l = 0; r = n - 1;
while(l <= r){
mid = (l + r) >> 1;
if(w[mid] < a[i]){
ret = mid;
l = mid + 1;
}
else r = mid - 1;
}
w[ret + 1] = a[i];
ans = max(ans,ret + 1);
}
return ans;
}