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。    

  • 相关阅读:
    19c 新特性: Hint Usage Reports详解
    byte buddy学习笔记
    彻底告别“人工+Excel”低效模式,传统制造业实现“一站式”数据化管理
    有的人想在上班之余做兼职,并且不想投入太多钱
    2019你为什不选择创业?是因为什么原因?
    深入理解pandas读取excel,txt,csv文件等命令
    劳务派遣公司是怎么盈利的?
    为什么穷人大多不敢创业?
    30多岁了仍一事无成,除了去工厂上班还有哪些出路?
    MySQL Change Data Directory
  • 原文地址:https://www.cnblogs.com/SueMiller/p/2243450.html
Copyright © 2011-2022 走看看