《训练指南》——7.25

Uva10635:

  有两个长度为p+1,q+1的序列A、B,每个序列的各个元素互不相同,且都是1~n^2的整数。两个序列的第一个元素均为1.求出A和B的最长公共子序列长度。

  分析:乍一看是典型的LCS问题,但是我们一考察数据发现n = 250 * 250结合LCS问题O(pq)的时间复杂度,使得这个问题需要必要的优化技巧。

  我们从题目描述出发,这里有一个很关键的要素是,两个序列各个序列中的元素互不相同,因此每个数据和其位置坐标将形成一一对应的关系,那么我们用num1[i]记录A序列中整数i的下标, 同时在输入B序列时,我们用num2[i]表示下标为i的整数x在A序列中的下标ai。那么现在我们考察序列num2[],我们会得到如下两组序列:

  x0 x1 x2 x3 …xi...

  a0 a1 a2 a3 …ai...

  1    2   3   4 …  i...

  其含义为,在A、B中共有的元素xi在序列A中的下标是ai , 在序列B中的下标是i。做这样一个复杂的数组的意义到底是什么呢?我们会发现,基于LCS的定义,联系下标,我们能够发现,公共子序列C中的各个元素在序列A、B中的下标序列均为是严格的递增,那么回到这个问题来,元素在序列B中的下标是1,2,3,4…显然是严格递增的了,也就是说,所谓的LCS问题,由于这道问题的特殊限制(每个序列的各个元素互不相同),我们可以将LCS问题转化成num2[]数组的LIS问题,而处理LIS问题我们有O(nlogn)的算法,而制作num1、num2两个数组的时间复杂度几乎是忽略不计的(伴随输入过程进行构造)。

  简单的参考代码如下。

 

#include<cstdio>

#include<cstring>

#include<algorithm>

using namespace std;

const int maxn = 250 * 250;

int S[maxn] , g[maxn] , dp[maxn];

int num[maxn];

const int INF = 1000000000;

 

int main()

{

     int t;
     int tt = 1;
     scanf("%d",&t);

     while(t--)

     {

        int n , p , q , x;

         scanf("%d%d%d",&n,&p,&q);

          for(int i = 1;i <= p + 1;i++)

          {

                scanf("%d",&x);

                num[x] = i;

          }

          int index = 0;

          for(int i = 1;i <= q + 1;i++)

          {

               scanf("%d",&x);

               if(num[x])  S[index++] = num[x];

          }

 

          for(int i = 0;i < index;i++)

                 g[i] = INF;

          int Max = 0;

          for(int i = 0;i < index;i++)

          {

                int k = lower_bound(g + 1 , g + index + 1 , S[i]) - g;

                dp[i] = k;

                g[i] = S[i];

                Max = max(Max , dp[i]);

          }

 

          printf("Case %d: %d\n",tt,Max);

          tt++;

     }

}

 

posted on 2016-07-25 11:24  在苏州的城边  阅读(181)  评论(0编辑  收藏  举报

导航