uva 10635 Prince and Princess(LCS问题转化成LIS问题O(nlogn))

题目大意:有两个长度分别为p+1和q+1的序列,每个序列中的各个元素互不相同,且都是1~n^2之间的整数。两个序列的第一个元素均为1。求出A和B的最长公共子序列长度。


分析:本题是LCS问题,但是p*q<=62500,O(pq)的算法显然会LE。在这里有一个条件,每个序列中的各个元素互不相同,所以可以把A中元素重新编号为1~p+1。例如,样例中A={1,7,5,4,8,3,9},B={1,4,3,5,6,2,8,9},因此把A重新编号为{1,2,3,4,5,6,7},则B就是{1,4,6,3,0,0,5,7}(在A中没有出现过的元素一定不会是公共子序列中的元素),其中0表示A中没有出现过,可以直接删去。这时B={1,4,6,3,5,7},元素的值代表着B中和原A中元素值相同的,在A中的位置。子序列的位置一定要是单调递增的,这样求得的最长子序列才相当于原A和B的最长公共子序列。由此,成功转化成LIS问题`(*∩_∩*)′。求出B的LIS即可,时间复杂度就可以优化到O(nlogn)了。

下面贴上代码(借鉴lrj巨犇的=-=)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 250*250;
const int INF = 1e9;
int s[maxn],g[maxn],d[maxn];
int num[maxn];  //num[x]为整数x的新编号,num[x]=0表示x没有在A中出现过

int main()
{
    int T;
    cin>>T;
    for(int kase=1;kase<=T;kase++)
    {
        int N,p,q,x;
        cin>>N>>p>>q;
        memset(num,0,sizeof(num));
        for(int i=1;i<=p+1;i++)
        {
            cin>>x;
            num[x]=i;
        }
        int n=0;
        for(int i=0;i<q+1;i++)
        {
            cin>>x;
            if(num[x]) s[n++]=num[x];
        }
        
        //求解s[0]...s[n-1]的LIS
        for(int i=1;i<=n;i++) g[i]=INF;
        int ans=0;
        for(int i=0;i<n;i++)
        {
            int k=lower_bound(g+1,g+n+1,s[i])-g;
            d[i]=k;
            g[k]=s[i];
            ans=max(ans,d[i]);
        }
        cout<<"Case "<<kase<<": "<<ans<<endl;
    }
    return 0;
}

关于lower_bound函数(二分查找函数),是STL库的,不懂的童鞋请看 http://blog.csdn.net/u012198382/article/details/24887181(lower_bound用法)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用动态规划空间优化的 C 代码: ```c #include <stdio.h> #include <string.h> #define MAX_N 1000 int dp[2][MAX_N+1]; int lcs(char* s1, char* s2, int len1, int len2) { int i, j; for (i = 0; i <= len1; i++) { for (j = 0; j <= len2; j++) { if (i == 0 || j == 0) { dp[i%2][j] = 0; } else if (s1[i-1] == s2[j-1]) { dp[i%2][j] = dp[(i-1)%2][j-1] + 1; } else { dp[i%2][j] = (dp[(i-1)%2][j] > dp[i%2][j-1]) ? dp[(i-1)%2][j] : dp[i%2][j-1]; } } } return dp[len1%2][len2]; } int main() { char s1[MAX_N], s2[MAX_N]; scanf("%s%s", s1, s2); int len1 = strlen(s1); int len2 = strlen(s2); printf("%d\n", lcs(s1, s2, len1, len2)); return 0; } ``` 以上代码将原来的二维数组 `dp` 改为了两个一维数组,使用滚动数组的方式实现状态转移。这样可以将空间复杂度从 O(n²) 优化为 O(n)。 以下是使用树状数组优化的 C 代码: ```c #include <stdio.h> #include <string.h> #define MAX_N 1000 int c[MAX_N+1]; int dp[MAX_N+1]; int lowbit(int x) { return x & (-x); } void update(int x, int v, int n) { while (x <= n) { if (dp[x] < v) { dp[x] = v; } x += lowbit(x); } } int query(int x) { int res = 0; while (x > 0) { if (dp[x] > res) { res = dp[x]; } x -= lowbit(x); } return res; } int lcs(char* s1, char* s2, int len1, int len2) { int i, j; for (i = 1; i <= len1; i++) { memset(c, 0, sizeof(c)); for (j = 1; j <= len2; j++) { if (s1[i-1] == s2[j-1]) { int v = query(j-1) + 1; update(j, v, len2); } } } return query(len2); } int main() { char s1[MAX_N], s2[MAX_N]; scanf("%s%s", s1, s2); int len1 = strlen(s1); int len2 = strlen(s2); printf("%d\n", lcs(s1, s2, len1, len2)); return 0; } ``` 以上代码使用了树状数组来维护前缀最长公共子序列的长度。时间复杂度为 O(nlogn)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值