详解最长不下降子序列

前言

最长不下降子序列大家应该都做过,都听过。这是一道经典的DP问题。

若用普通的解法,时间复杂度高达O(n²)

但是,还有一种更加神奇的方法,时间复杂度只有O(nlogn)

下面,我希望能够通过自己的理解,写出一篇让大家明白的博文。

一、最长不下降子序列

什么是最长不下降子序列?指的是从序列中选出若干个数组成一个新的序列,不改变他们的队伍的顺序,要求新的序列里xi≤xi+1≤xi+1.....

举个例子{4,6,5,7,3},最长不下降子序列就是{4,6,7}。同样的,还有最长不上升子序列。

二、O(n²)解法,DP

我们设每一个数为阶段,因为没有后效性,可以用DP

设F[i]表示选到了第i个数,1~i中最长不下降子序列的长度是F[i]

怎么转移?我们只要从j=1~i-1枚举一个数,因为当前F[i]为必选,所以我们假设枚举的这个数是上一个最长不下降子序列的数。

当然,要满足a[i]≥a[j]

很明显,在可以选的情况下,越大越好

所以F[i]=max(F[j]) (i=1~n,j=1~i-1)

for i:=1 to n do
                f[i]:=1;//一开始,F[i]=1,因为他们必选自己

        for i:=2 to n do//第一个必定为1,所以从第二个开始寻找
        begin
                max:=0;
                for j:=1 to i-1 do
                        if (a[i]>=a[j])and(f[j]>max) then//在前面寻找一个比他小(或者等于)的,一个最大的F[i]
                                max:=f[j];
                f[i]:=max+f[i];
        end;
最后的结果就是max(F[i])

时间复杂度O(n²)

三、O(nlogn)解法

表示网上大部分都是用两种算法,一种是二分,一种是设立temp数组

temp数组

这个temp数组是解题关键。temp[i]表示长度为i的最长不下降子序列,结尾的元素(第i个元素)的最小值是多少。tot表示当前最长不下降子序列的长度

如果a[i]≥temp[tot],说明可以摆在第tot+1个位置,那么tot+1,temp[tot+1]=a[i]

如果a[i]<temp[tot],那么说明当前不能摆在第tot+1的位置。我们从temp[1]...temp[tot]中找出一个j,使得a[i]≥temp[j],那么,a[i]可以放在第j+1个位置,那么更新temp[j+1],这样就可以更新最小值了。

我开始就在想,为什么用a[i]来更新temp[j+1],a[i]一定比temp[j+1]小,因为前面已经保证是最大的j了,所以temp[j+1]一定是大于a[i]的。

这样,就可以提高效率了。

f[1]:=a[1];//最开始的数
sum:=1;
for i:=2 to n do
begin
        bz:=false;
        if a[i]>=f[sum] then
        begin
                inc(sum);
                f[sum]:=a[i];
                bz:=true;
        end
        else
        for j:=sum downto 1 do
                if f[j]>=a[i] then
                begin
                        f[j+1]:=a[i];
                        bz:=true;
                        break;
                end;

        if not bz then
                f[1]:=a[i,2];

end;
writeln(sum);
二分:

二分是较上面的一种nlogn方法的优化,找出一个j,这个j最大,就可以用二分来实现。

代码不写了吧,就是上面的循环修改成二分。效率也会快很多哦。


猜你喜欢

转载自blog.csdn.net/fengyingjie2/article/details/54971432