最长不降子序列(LIS)算法
自己乱搞了个数据
输入
10
1 3 7 3 6 1 3 2 4 5
输出
6
显然上述数据中最长不降子数列为{1,3,3,3,4,5},长度为六。
一、暴力dp
很容易就能想到的方法:
对于一个序列A[1..n],f[i]表示从Ai到An的最长不降子序列,
则转移方程为:f[i]:=max(f[i],f[j+1])【i:=n downto 1;j:=i+1 to n;f[i]初始为1】
时间复杂度为 O(n^2)
代码:
var n,i,j,max:longint;
a,f:array[1..10000]of longint;
function max1(a,b:longint):longint;
begin
if a>b then exit(a)
else exit(b);
end;
begin
readln(n);
for i:=1 to n do
read(a[i]);
for i:=n downto 1 do
begin
f[i]:=1;
for j:=i+1 to n do
if a[j]>=a[i] then
f[i]:=max1(f[j]+1,f[i]);
end;
for i:=1 to n do
if f[i]>max then max:=f[i];
writeln(max);
end.
显然,在数据>10000时会T。
二、单调队列二分优化
令一个数组B[1..lenb]
b[i]表示从1到i中最长不降子序列中最后一个元素
可以发现B是单调不降的
我们每读入一个Ai,只需维护B单调不降即可
维护用二分查找,查找b[i]满足b[i-1 <= x < b[i+1],复杂度 O(log n)
最后输出lenb即可
总复杂度O(n log n)
代码:
var n,i,x,l,r,w,mid:longint;
a:array[0..1000000]of longint;
begin
readln(n);
for i:=1 to n do
begin
read(x);
if x>=a[w] then
begin
inc(w);a[w]:=x;
end
else
begin
l:=1;r:=w;
while l<r do
begin
mid:=(l+r) div 2;
if a[mid]<=x then l:=mid+1
else r:=mid;
end;
a[r]:=x;
end;
end;
writeln(w);
end.
谢谢!