一中OJ | #1434 序列的LCS | UVa 10635 Prince and Princess | 序列型动态规划
时限 1000MS/Case 内存 64MB/Case
题目描述
有两个长度分别为p+1和q+1的序列,每个序列中的各个元素互不相同,且都是1~n^2之间的整数。两个序列的第一个元素均为1。求出A和B的最长公共子序列长度。
输入格式
输入的第一行为数据组数T。每组数据包含3行,第一行为3个整数n, p;第二行包含序列A,其中第一个数为1,其元素两两不同,且都是1~n2之间的整数;第三行包含序列B,格式同序列A。
输出格式
对于每组数据,输出A和B的最长公共子序列的长度。
样例输入
1
3 6 7
1 7 5 4 8 3 9
1 4 3 5 6 2 8 9
样例输出
4
数据范围
T≤10
2≤n≤250,1≤p,q≤n^2。
----------------------------------------------------------
题目分析
首先吐槽一句,这道题在OJ里面叫做序列的LCS,真能放烟雾弹 = =
众所周知,LCS的常规DP复杂度为O(mn),刚开始拿到此题一看n≤250,心里一想哇塞好简单
转眼一看,后面有一个序列长度n^2啊...再一想,这个专题是序列型动态规划,而LCS是匹配型动态规划
那么这道题要么就是hb放错地方了,要么就是真的是一道序列dp题...就把它当成序列dp看吧(其实它本来就是序列dp啦)
分析数据,首先第一个位置是1,后面数据数字都小于n^2(62500),序列长度也是62500,并且序列中元素互异
这么多限制条件,仿佛是可以转换LIS了,LIS正好有nlgn的算法,那么它怎么转呢
拿样例举例,将序列A={1,7,5,4,8,3,9}做映射到A'={1,2,3,4,5,6,7},再用B以A’的映射表转换一遍,得到B’={1,4,6,3,0,0,5,7}
观察A'与B'两个序列,序列A'是绝对升序的,并且序列A'与A是一一映射关系,B'与B和A’与A的映射是完全相同的
那么A'与B'的LCS岂不就是A与B的LCS嘛
为什么这样转换是对的呢?因为上面提到数字都小于n^2,序列长度也小于n^2,那么也不会有元素重复出现的情况或者元素很大不好做映射的情况了
就相当于是元素互异的条件让我们能做映射(映射要求就要一一对应),序列元素的大小让我们好做映射(谁想把下标开到巨大然后跑map费时间呢)
因为A'是一个升序序列,那么找出B'中最长的升序子序列,映射回去,肯定B与A中相应元素也是对应的,这样找B‘的LIS就相当于找了AB的LCS啦
----------------------------------------------------------
代码
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <cctype>
#include <iomanip>
#define inf 0x7f7f7f7f
using namespace std;
int n,p,q,l,a1[250*250+5],a2[250*250+5],mp[250*250+5],t[250*250+5];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
l=1;
memset(a1,0,sizeof(a1));
memset(a2,0,sizeof(a2));
memset(mp,0,sizeof(mp));
memset(t,0,sizeof(t));
scanf("%d%d%d",&n,&p,&q);
for(int i=1;i<=p+1;i++) scanf("%d",&a1[i]);
for(int i=1;i<=q+1;i++) scanf("%d",&a2[i]);
for(int i=1;i<=p+1;i++) mp[a1[i]]=i;
for(int i=1;i<=q+1;i++) a2[i]=mp[a2[i]];
for(int i=1;i<=q+1;i++)
{
int p=lower_bound(t,t+l,a2[i])-t;
if(p==l) l++;
t[p]=a2[i];
}
cout<<l-1<<endl;
}
return 0;
}