最长上升子序列

最长上升子序列裸题

【题目描述】
有N个数,求最长上升子序列。
【输入格式】
第一行一个整数N
第二行N个整数表示N个数
【输出格式】
一个整数表示最长上升子序列的最大长度
【样例输入】
5
1 3 2 5 6
【样例输出】
4
【样例解释】
1-3-5-6或1-2-5-6
【数据范围】
对于70%的数据,1<=N<=1000
对于100%的数据,1<=N<=100000
【分析】
对于70%的数据,不难想到令f[i]表示以第i个数结尾的最长上升子序列的长度,第i个数一定是接在之前的某一个序列的后面,故枚举j=1..i-1找到最大的f[j],f[i]:=f[j]+1,最后输出f[1..n]中的最大值即可。
那对于100%的数据呢?
从数据范围就可以看出,应该用n log n的算法解决。
维护一个栈Stack,若当前数k大于栈顶的数就压栈,否则二分查找出一个mid使得Stack[mid-1] <= k < Stack[mid],将k赋给Stack[mid]即可。最后输出栈中元素的个数。
可能有人感到奇怪:无序的序列也能二分查找?注意,这里是在Stack里查找,在这个栈里所有元素都是有序的,这从k的赋值也可以体现。
但是如果要输出该序列中的元素,这个算法就要修改了。

var
    n,i,t:longint;
    a,s:array[-1..100000] of longint;
function find(l,r:longint):longint;
var mid:longint;
begin
    while l<=r do begin
        mid:=(l+r) shr 1;
        if (s[mid]>a[i])and(s[mid-1]<=a[i]) then exit(mid);
        if s[mid]<a[i] then l:=mid+1 else r:=mid-1;
    end;
    exit(l);
end;
begin
    read(n);
    for i:=1 to n do read(a[i]);
    t:=1;
    s[1]:=a[1];
    for i:=2 to n do
        if a[i]>s[t] then begin
            inc(t);
            s[t]:=a[i];
        end
        else s[find(1,t)]:=a[i];
    write(t);
end.

下面看一些例题。

友好城市

【题目描述】
有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。没对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航道不相交的情况下,被批准的申请尽量多。
【输入格式】
第1行,一个整数N(1<=N<=100000),表示城市数。
第2行到第n+1行,每行两个整数,中间用一个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。(0<=xi<=10000)
【输出格式】
仅一行,输出一个整数,表示政府所能批准的最多申请数。
【样例输入】
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
【样例输出】
4
【分析】
这题很明显,对a数组排序,然后对b数组求最长上升子序列即可。
算法1:双重循环

uses math;
var
  i,j,n,ans,maxx:longint;
    a,b,f:array[0..5001]of longint;
procedure qsort(l,r:longint);
var
  i,j,temp,mid:longint;
begin
  i:=l; j:=r;
  mid:=a[(l+r) div 2];
  repeat
    while a[i]<mid do inc(i);
    while a[j]>mid do dec(j);
    if i<=j then
    begin
      temp:=a[i];a[i]:=a[j];a[j]:=temp;
      temp:=b[i];b[i]:=b[j];b[j]:=temp;
      inc(i);dec(j);
    end;
  until i>j;
  if l<j then qsort(l,j);
  if i<r then qsort(i,r);
end;
begin
  readln(n);
    for i:=1 to n do readln(a[i],b[i]);
    qsort(1,n);
    f[1]:=1;
    ans:=1;
    for i:=2 to n do begin
      maxx:=0;
      for j:=i-1 downto 1 do
          if b[j]<b[i] then maxx:=max(maxx,f[j]);
        f[i]:=maxx+1;
      ans:=max(ans,f[i]);
    end;
    write(ans);
end.

算法2:二分查找

uses math;
var
  i,j,n,t:longint;
    a,h,f,s:array[0..100001]of longint;
function find(l,r:longint):longint;
var mid:longint;
begin
    while l<=r do begin
        mid:=(l+r) shr 1;
        if (a[mid]>h[i])and(a[mid-1]<=h[i]) then exit(mid) else if a[mid]<h[i] then l:=mid+1 else r:=mid-1;
    end;
    exit(l);
end;
procedure qsort(l,r:longint);
var
  i,j,temp,mid:longint;
begin
  i:=l; j:=r;
  mid:=a[(l+r) div 2];
  repeat
    while a[i]<mid do inc(i);
    while a[j]>mid do dec(j);
    if i<=j then
    begin
      temp:=a[i];a[i]:=a[j];a[j]:=temp;
      temp:=h[i];h[i]:=h[j];h[j]:=temp;
      inc(i);dec(j);
    end;
  until i>j;
  if l<j then qsort(l,j);
  if i<r then qsort(i,r);
end;
begin
  readln(n);
    for i:=1 to n do readln(a[i],h[i]);
    qsort(1,n);
    t:=1;
    fillchar(a,sizeof(a),0);
    a[1]:=h[1];
    for i:=2 to n do
        if h[i]>a[t] then begin
            inc(t);
            a[t]:=h[i];
        end
        else a[find(1,t)]:=h[i];
    writeln(t);
end.

箱子嵌套

【题目描述】
考虑一个给出为N维空间“箱子”的尺度问题。当有两个尺度时,箱子(2,3)表示这个箱子长度为2个单位,宽度为3个单位。当有三个尺度时,箱子(4,8,9)表示这个箱子长度是4个单位,宽度是8个单位,高度是9个单位。当它有6个尺度时,也许,它表现的是不易了解的箱子(4,5,6,7,8,9);但是我们能分析这个箱子的特性,例如它的各个尺度。在这个问题中你将分析许多关于N维空间的箱子,并解决这些箱子的最大的嵌套问题。箱子D =(d1,d2,…,dn),箱子E = (e1,e2,…,en),di和ei都可以重新排列整理,那么当它们重新排列整理后,箱子D所有的尺度都小于箱子E所有的尺度,那么箱子D可以放入箱子E中。
例如,箱子D =(2,6),当D重新排列整理成(6,2),则可放入箱子E =(7,3)中,此时D每个尺度都小于E的相应尺度。如果D经过重新整理后D =(9,7,5,3),则不可以放入箱子E =(10,8,6,2)中,但是F =(9,5,7,1),重新整理后F =(9,7,5,1),即符合F放入E的条件。
【输入格式】
第一行有两个整数,第一个整数为箱子的总数K,第二个整数为每个箱子的空间维数N。以下K行,每行表示一个箱子,有N个尺度数据,数据间用一个或数个空格分开。(K ≤ 100000,N ≤ 10)
【输出格式】
一个整数,输出最大箱子嵌套数目。
【样例输入】
5 2
3 7
8 10
5 2
9 11
21 18
【样例输出】
5
【分析】
和上一题很相似,只不过上一题只有2个维度,而这题有N个维度。
算法1:读入的时候把1个箱子的N个维度排序,然后按照任意一个维度将K个箱子排序,然后就是最长上升子序列了。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
int a[10010][10];
int f[10010];
int n,m;
void qsort(int l,int r){
    int i=l,j=r,mid=a[(l+r)/2][1];
    while (i<=j) {
        while (a[i][1]>mid) i++;
        while (a[j][1]<mid) j--;
        if (i<=j) {
            for (int k=1;k<=m;k++) {
                int tmp=a[i][k];
                a[i][k]=a[j][k];
                a[j][k]=tmp;
            }
            i++;j--;
        }
    }
    if (l<j) qsort(l,j);
    if (i<r) qsort(i,r);
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++) scanf("%d",&a[i][j]);
        for (int j=1;j<m;j++)
            for (int k=j+1;k<=m;k++)
                if (a[i][j]>a[i][k]) { int tmp=a[i][j];a[i][j]=a[i][k];a[i][k]=tmp; }
    }
    qsort(1,n);
    f[1]=1;
    for (int i=2;i<=n;i++){
    f[i]=1;
        int max=0;
        for (int j=1;j<=i;j++){
            bool flag=true;
            for (int k=1;k<=m;k++) if (a[i][k]>=a[j][k]) flag=false;
          if (!flag) continue;
            if (max<f[j]+1) max=f[j]+1;
        }
        if (f[i]<max) f[i]=max;
    }
    int max=0;
    for (int i=1;i<=n;i++) if (max<f[i]) max=f[i];
    cout<<max;
}

算法2:排序之类的跟算法1相同,不同的只有多了一个二分查找。这里用指针实现更方便。代码请支付¥100查看。

posted @ 2016-12-11 13:53  JRX2015U43  阅读(391)  评论(0编辑  收藏  举报