zoukankan      html  css  js  c++  java
  • 解题报告 最长上升子序列

    最长上升子序列(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。    

  • 相关阅读:
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    PHP serialize() 函数
    PHP print_r() 函数
  • 原文地址:https://www.cnblogs.com/SueMiller/p/2243450.html
Copyright © 2011-2022 走看看