解题报告 最长上升子序列

最长上升子序列(lis.pas/c/cpp)

 

【题目描述】

给出一个长度为N的整数序列,求出包含它的第K个元素的最长上升子序列。

 

【输入格式】

第一行两个整数N,K

第二行N个整数

 

【输出格式】

一个整数L如题目所说的序列长度。

 

【输入样例】

8 6

65 158 170 299 300 155 207 389

 

【输出样例】

4

 

【数据范围】

0<N<=200 000,0<K<=N

 

【hint】

    写出正确部分数据程序的同学会得到部分分。

 

 

首先,以 为分界点,在前后两段分别求一次最长不上升子序列(nlogn),要求,在他前面的子序列的最后一项要比他小,在他后面的子序列的第一项要比他大。(因为要把这个 a[k] 接在中间)。

 

代码(SueMiller

var

    n,i,l,r,x,mid,kk,ans,temp:longint;

    f:array[0..200010]of longint;

a:array[0..200010]of longint;

begin

    assign(input,'lis.in');reset(input);

assign(output,'lis.out');rewrite(output);

 

    read(n,kk);

for i:=1 to n do

  read(a[i]);

    for i:=1 to kk-1 do

    begin

      if a[i]>=a[kk] then continue;

  x:=a[i];

      l:=1;r:=f[0];

      while l<r do

        begin

          mid:=(l+r)shr 1;

          if f[mid]<x then   

            l:=mid+1 else r:=mid;

        end;

      if (l>r)or((l=r)and(x>f[f[0]]))then 

        begin

          f[0]:=f[0]+1;

          f[f[0]]:=x;

          l:=f[0];

        end else

          if x<f[l] then 

            f[l]:=x;

     end;

    ans:=f[0];

 

fillchar(f,sizeof(f),0);

    for i:=kk+1 to n do

    begin

      if a[i]<=a[kk] then continue;

  x:=a[i];

      l:=1;r:=f[0];

      while l<r do

        begin

          mid:=(l+r)shr 1;

          if f[mid]<x then   

            l:=mid+1 else r:=mid;

        end;

      if (l>r)or((l=r)and(x>f[f[0]]))then 

        begin

          f[0]:=f[0]+1;

          f[f[0]]:=x;

          l:=f[0];

        end else

          if x<f[l] then 

            f[l]:=x;

     end;

 ans:=ans+f[0]+1;

 writeln(ans);

 

 close(input);close(output);

end.

 

 

 

PS : 最长 XX 子序列 nlogn  【转】

最长上升、下降子序列

var

    n,i,l,r,x,mid:longint;

    f:array[0..10000]of longint;

begin

    read(n);

    for i:=1 to n do

    begin

      read(x);

      l:=1;r:=f[0];

      while l<r do

        begin

          mid:=(l+r)shr 1;

          if f[mid]<x then   //若求最长下降子序列则是f[mid]>x

            l:=mid+1 else r:=mid;

        end;

      if (l>r)or((l=r)and(x>f[f[0]]))then //若求最长下降子序列则是x<f[f[0]]

        begin

          f[0]:=f[0]+1;

          f[f[0]]:=x;

          l:=f[0];

        end else

          if x<f[l] then //若求最长下降子序列则是x>f[l]

            f[l]:=x;

     end;

    write(f[0]);

end.

引用Matrix67的话(求最长上升子序列):

     f表示长度为i的上升子序列最后一个数最小是多少。显然数组f是单增的。

     读到一个新的数x后,找到某个i使得x>f[i]x<=f[i+1],于是用x去更新f[i+1];特别地,如果所有的f[i]都小于x,则增加f的长度。

     最后看f数组有多长就行了。

     由于f单增,所以查找i时可以用二分查找,因此时间复杂度为O(nlogn)

     举个例子,假如序列为 3 2 8 6 7 4 5 7 3,则f数组的变化过程如下:

     3

     2

     2 8

     2 6

     2 6 7

     2 4 7

     2 4 5

     2 4 5 7

     2 3 5 7

     最后,f的长度达到4,因此答案为4

     注意,最后的f数组不一定是最长上升子序列的一个方案。

 

 

 

 

 

 

 

 

 

 

 

 

最长不上升、下降子序列

 题目:求 1 3 5 2 4 10 4 8 的最大不下降子序列

   首先我们定义一个数组RES,则RES[i] 表示当子序列长度为I时子序列最后一位的最小值。

    我们用LENGTH来记录这个数组的有效长度。

   处理方法:当我们新读入一个数W,我们比较其与RES[LENGTH]的大小

      1)W>=RES[LENGTH]; 那么 LENGTH:=LENGTH+1; RES[LENGTH]:=W; 即在已求出的最长序列RES[LENGTH]后在接上W,则长度加1

      2)W<RES[LENGTH] 那么从1..RES[LENGTH] 里面找到一个最大的数RES[J](特别注意,若有相同的数如:要插入RES=[1,2,4,4,4,6] 则取最后一个4,这一点表现在后

 

面程序中mid>x 而不能去等上),使其满足RES[J]<W,

并且我们更新RES[J+1]:=W;

即找到第一个(从左往右数)比带插入的大的数,然后更新为待插入的数。

    最后当我们处理完原序列所有数字后,LENGTH 即为最大不下降子序列的长度。

    当我们完成2)这个步骤的时候我们采用2分查找的方法,则可将时间复杂度从n降为Logn

   需要注意的是,数组RES在算法结束后记录的并不是一个符合题意的最长上升子序列!

   程序实现如下:

    (以下程序已在FREE PASCAL 中编译并运行成功)

  

program LIS;

const

  inf='LIS.in';

  outf='LIS.out';

  maxn=100;

 

var

  x:array[1..maxn] of integer;

  res:array[0..maxn] of integer;

  length,n,temp:integer;

 

procedure init;

var i:integer;

begin

  readln(n);

  for i:=1 to n do

    read(x[i]);

end;

 

procedure find(v1,v2,x:integer); {二分查找,即找到第一个比待插入数大的数}

var mid:integer;

begin

  if v1=v2 then begin temp:=v1; exit; end;

  mid:=res[(v1+v2) div 2];

  if mid>x then //注意不能取等号,若取了等号就会出现把相同的数覆盖掉的问题

    find(v1,(v1+v2) div 2,x)

  else

    find(((v1+v2) div 2)+1,v2,x);

end;

 

procedure main;{LIS}

var i:integer;

begin

  length:=0;

  for i:=1 to n do

    begin 

      if x[i]>=res[length] then

        begin

          inc(length);

          res[length]:=x[i];

        end

      else begin

        temp:=0; 

        find(1,length,x[i]);

        res[temp]:=x[i]

      end;

    end;

end;

 

procedure outit;

begin

  writeln(length);

end;

begin

  assign(input,inf);assign(output,outf);

  reset(input);rewrite(output);

  init;

  main;

  outit;

  close(input);close(output);

end.   

    得出答案为5。    

posted @ 2011-11-09 18:04  木小漾  阅读(788)  评论(0编辑  收藏  举报