UVA 10635 Prince and Princess(最长上升子序列)

该博客介绍了如何解决UVA 10635问题,即在n*n的格子中寻找两个不同序列的最长公共上升子序列。由于输入限制,博主提出使用O(n log n)的算法,通过将输入映射到递增序列并寻找第二个人映射的最长上升子序列。博主详细解释了动态规划的方法,包括如何维护单调递增的dp数组,并利用二分查找或lower_bound进行更新,从而确定最长公共子序列的长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:有n*n的格子用1-n*n不同的数表示,第一个人输入p+1长度的数,第二个人输入q+1个不同的数,问他们最长公共子序列最长是多长。

 分析:由于(0<=n<=250,n*n最大=250*250),用O(n^2)的算法是不能解决的,需要一种nlog(n)的算法。由于这里比较特殊,每个人输入的数都是不重复的,那么就把第一个人输入的数映射成1,2,3……,第二个人输入的数映射对应第一个人输入数的映射。这样,第一个人的映射是递增的,那么我只需要找到第二个人的映射的最长上升子序列的个数就是ans。

那么怎么求这个最长上升子序列呢?假设求1,4,6,3,5,7,最长上升子序列

dp[i]:表示长度为i,最小的那个数

①加入1,dp[1]=1;②加入4,dp[2]=4;③加入6,dp[3]=6;④加入3,dp[2]=3;⑤加入5,dp[3]=5;⑥加入7,dp[4]=7;

所以最终ans=4,这里,应为dp[]里存的数都是单调递增的,因此可以二分查找或lower_bound(加入的数)的id,更新dp[id]。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <queue>
//http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82285#problem/E
#include <stack>
#include <vector>
#include <string>
#include <string.h>
#include <map>
#include <set>
using namespace std;
#define maxn 250*251
#define inff 1000000000
int a[maxn],b[maxn],dp[maxn];

int lower_bound(int aa[],int size,int k)//查找第一个>=k的数的id
{
    int l,r,mid;
    l=0;r=size-1;
    while(l <= r)
    {
      mid=(l+r)/2;
    if(aa[mid] >= k) r = mid - 1;
    else l = mid + 1;
  }
  return l;
}


int main()
{
    int t,k,n,num1,num2,i,m,tt=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&num1,&num2);
        num1++;
        num2++;
        memset(a,-1,sizeof a);
        memset(b,-1,sizeof b);
        for(i=1;i<=num1;i++)
        {
            scanf("%d",&m);
            a[m]=i;//把第一个人的数映射成1,2,3……
        }
        k=1;
        for(i=1;i<=num2;i++)
        {
            scanf("%d",&m);
            if(a[m]!=-1)
            b[k++]=a[m];//第二个人输入的数,参照第一个人的映射,存下该数的映射
        }
        for(i=0;i<maxn;i++)
        {
            dp[i]=inff;
        }
        dp[0]=-1;//令dp[0]最小
        for(i=1;i<k;i++)
        {
            int j=lower_bound(dp,maxn-1,b[i]);
            dp[j]=b[i];
        }
        for(i=1;i<maxn;i++)
        {

            if(dp[i]==inff)
            {
                printf("Case %d: %d\n",tt++,i-1);
                break;
            }
        }


    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值