• 1264: [AHOI2006]基因匹配Match(动态规划神题)


    1264: [AHOI2006]基因匹配Match

    题目:传送门

    简要题意:

      给出两个序列。每个序列都由n种不同的数字组成,保证每个序列种每种数字都会出现5次(位置不一定一样),也就是序列长度为5*n啦。

      求这两个序列的最长公共子序列。

    题解:

      假的(nlogn)最长公共子序列算法

      本蒟蒻看完题:

        woc!大水题,这不是就是动态规划求最长公共子序列吗~

      看完数据范围...ORZ那么大!!!最长公共子序列的基础算法要(n^2)...完了...不会...瞎搞???

      这么皮,怎么破!

      tkj神犇:so easy~~(orz...%%%)

      好的,%完师兄...

      真正的正解:

      本题的突破口其实就在于每种数只出现5次。

      那么我们可以先把第一个数列种每种数出现的位置用pos[]记录,然后放到第二个数列。

      举个栗子,就拿样例来说:

      第一个:1 1 2 2 1 1 2 1 2 2

      那么pos[1]=1,2,5,6,8       pos[2]=3,4,7,9,10

      然后放回第二个数列:1 2 2 2 1 1 2 2 1 1 

      用一个新的s[]记录新数列,s[]=8 6 5 2 1,10 9 7 4 3, 10 9 7 4 3...(以此类推)

      那么我们只需要求出这个新数列的最长上升子序列就ok啦~(原理十分简单,模拟一下就ok)

      这里还有一个小细节需要注意:不难发现,我们更新新数列的时候每种数字的位置都是按倒序放的,这样就可以避免在第二个数列中的一个位置重复选择(重点)。

      好啦,这样子时间复杂度就为(nlongn)

      但是...是假的...因为要是每种数不止出现五次...那就ORZ吧!

    AC代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cmath>
     5 #include<algorithm>
     6 #define qread(x)x=read();
     7 using namespace std;
     8 inline int read()
     9 {
    10     int f=1,x=0;char ch;
    11     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    13     return f*x;
    14 }
    15 struct node1
    16 {
    17     int pos[10],id;
    18     node1(){id=0;}
    19 }a[21000];
    20 struct node2
    21 {
    22     int pos[10],id;
    23     node2(){id=0;}
    24 }b[21000];
    25 int s[510000];
    26 int n,N,len;
    27 bool cmp(int n1,int n2)
    28 {
    29     return n1>n2;
    30 }
    31 int f[510000];//表示长度为i的最长上升子序列的末尾(最小) 
    32 int erfen(int x)
    33 {
    34     int l=1,r=len,ans=1;
    35     while(l<=r)
    36     {
    37         int mid=(l+r)/2;
    38         if(f[mid]>=s[x])
    39         {
    40             ans=mid;
    41             r=mid-1;
    42         }
    43         else l=mid+1;
    44     }
    45     return ans;
    46 }
    47 int main()
    48 {
    49     qread(n);
    50     for(int i=1;i<=n*5;i++)
    51     {
    52         int x;qread(x);
    53         a[x].id++;
    54         a[x].pos[a[x].id]=i;
    55     }
    56     for(int i=1;i<=n;i++)sort(a[i].pos+1,a[i].pos+5+1);
    57     for(int i=1;i<=n*5;i++)
    58     {
    59         int x;qread(x);
    60         int k;
    61         k=a[x].id;
    62         for(int j=i*5-4;j<=i*5;j++)
    63         {
    64             s[j]=a[x].pos[k];
    65             k--;
    66         }
    67     }
    68     N=n*25;
    69     f[1]=s[1];len=1;
    70     for(int i=2;i<=N;i++)
    71     {
    72         if(s[i]>f[len])
    73             f[++len]=s[i];
    74         else
    75         {
    76             int j=erfen(i);
    77             f[j]=s[i];
    78         }
    79     }
    80     printf("%d
    ",len);
    81     return 0;
    82 }
  • 相关阅读:
    linux 回收站 路径
    Linux 让进程在后台可靠运行的几种方法
    用marquee和div+js实现首尾相连循环滚动效果
    轻型数据库SQLite结合PHP的开发
    linux系统权限修复——学生误操作!
    2009级 毕业设计 题目
    linux下硬盘uuid查看及修改设置
    创建网站地图
    用上下左右箭头键在textbox中的光标跳转
    SHELL中时间的比较
  • 原文地址:https://www.cnblogs.com/CHerish_OI/p/8037168.html
Copyright © 2020-2023  润新知