浅析最长不下降子序列一题的方法

对于最长不下降子序列一题,问题描述如下:

有长度为N的序列:
A1 A2 …..An
求最长不下降子序列:Ai1,Ai2,,,,,Aik, 其中ai1<=ai2<=.....<=aik
求最长不下降子序列的长度 


一看最长不下降子序列,最先想到的肯定是O(n²)的DP解法,当然,这里还是写下代码,加深理解:

var
        a,f:array[1..1000] of longint;
        n,i,j,ans:longint;
function max(x,y:longint):longint;
begin
        if x>y then exit(x) else exit(y);
end;
begin
        readln(n);
        for i:=1 to n do
                read(a[i]);

        for i:=n-1 downto 1 do
                for j:=i+1 to n do
                        if a[i]<=a[j] then
                        begin
                                f[i]:=max(f[i],f[j]+1);
                                ans:=max(ans,f[i]);
                        end;

        writeln(ans+1);
end.


(以下简称最长不下降子序列为LS)

但是,我们知道,其实这种方法是很笨的,因为有很多数明显不能构成一个最优的序列,我们还拿它去比较,就是很多余的。

但这不能构成一个最优序列是什么意思呢?


假设当前构成的LS的长度为3,最后一位的数为7,那如果现在又加进一个数,也可以与前面的数构成长度为3的LS,那么,到底是以6结尾好?还是7结尾好呢?毋庸置疑。。


当然是6.


当然是最后一位越小越好啊,这样后面构成LS的长度才有可能更优,根据这一思路,我们就可以大概知道我们到底怎么去优化LS了,那到底怎们实现呢?


所以我们可以多建一个f数组表示以长度为i的最长不下降子序列的最后一个数是多少。每次增加一个数x,我们都需要去更新f数组的值,找到一个刚好的下标i使得f[i]>x,则可把f[i+1]赋值为x.


很明显这种方法是易证的.


代码:

var
        a,f:array[1..100000] of longint;
        n,i,k,len:longint;
function find(len,n:longint):Longint;
var
        l,r,mid:longint;
begin
        l:=1; r:=len;
        while l<=r do
        begin
                mid:=(l+r) div 2;
                if f[mid]=n then exit(mid);
                if f[mid]>n then r:=mid-1 else l:=mid+1;
        end;
        exit(l);
end;
begin
        readln(n);
        for i:=1 to n do
                read(a[i]);

        len:=1;
        f[1]:=a[1];

        for i:=2 to n do
        begin
                k:=find(len,a[i]);
                f[k]:=a[i];
                if k>len then len:=k;
        end;
        writeln(len);
end.



但是,对上面程序做一番思考之后,会发现,此方法只是使用于最长递增子序列,即所输入的数不能有相等的数,但我们要探讨的是LS,所以,我们可以只需对上述程序做一个当相等的时候暴力求位置即可,但这种方法当最坏情况的时候效率还是有可能接近O(n²)的.

当然,我们也可以对二分改动一下,把L,r更新时有些数会重复的这些情况做相应的改变也可以 .最坏复杂度也是O(n²)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值