poj 2250

这是我的第一篇博客,接触ACM也快半年了,一直没写过一篇像样的解题报告,惭愧。

题目链接 http://poj.org/problem?id=2250

题意很简单,给出n组case,每组case由两部分组成,分别包含若干个单词,都以“#”当结束标志,要求输出最长的子序列。

其实在之前也做过类似的题目,给出两个字符串,问最长子序列的长度,很基础的一道dp,这道题可以说是那道题的升级版,一个是把字符换成了字符串,另一个难点是要求输出最长的子序列,那么就和原来的题目不同了,不是一个dp二维数组就可以的了,必须要记录路径,我的做法可能比较弱,但我感觉应该比较容易理解和接受吧,下面说一下我的思路和做法。

输入就不说了,首先是要求出最长子序列的长度,dp思想,dp[i][j]代表第一部分的前i个单词和第二部分的前j个单词的最长子序列,状态转移方程是如果第i个单词和第j个单词相同,dp[i][j]=dp[i-1][j-1]+1,否则dp[i][j]=max(dp[i-1][j],dp[i][j-1]);这样循环下去,最后结果就是最长子序列的长度,然后要做的就是记录路劲,我的做法是开一个三维数组ans[i][j][k],ans[i][j]数组中存储第一部分前i个和第二部分前j个相同的单词在第一部分中的位置,开始初始化都是空的,如果第一部分第i个和第二部分第j个单词相同,则把ans[i-1][j-1]中的元素都拷过来,一共有dp[i][j]-1个元素,再在第dp[i][j]个位置上加入i,如果单词不相同,看dp[i-1][j]和dp[i][j-1]谁更大,就把对应的ans的元素都拷到ans[i][j]中,最后,根据数组ans[第一部分单词数][第二部分单词数]之中的元素把第一部分中相应位置的单词依次输出来就可以了。

本来以为可能会超时,谁知道结果0ms,呵呵了。。。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int ans[105][105][105],flag[105][105];
char s1[105][35],s2[105][35];
int len1,len2;

void dp(){
    memset(ans,0,sizeof(ans));
    memset(flag,0,sizeof(flag));
    for(int i=1;i<len1;i++){
        for(int j=1;j<len2;j++){
            if(!strcmp(s1[i],s2[j])){
                for(int k=0;k<flag[i-1][j-1];k++)
                    ans[i][j][k]=ans[i-1][j-1][k];
                flag[i][j]=flag[i-1][j-1]+1;
                ans[i][j][flag[i][j]-1]=i;
            }
            else{
                if(flag[i-1][j]>flag[i][j-1]){
                    flag[i][j]=flag[i-1][j];
                    for(int k=0;k<flag[i-1][j];k++)
                        ans[i][j][k]=ans[i-1][j][k];
                }
                else{
                    flag[i][j]=flag[i][j-1];
                    for(int k=0;k<flag[i][j-1];k++)
                        ans[i][j][k]=ans[i][j-1][k];
                }
            }
        }
    }
}

void print(){
    for(int i=0;i<flag[len1-1][len2-1];i++)
        if(i) printf(" %s",s1[ans[len1-1][len2-1][i]]);
        else printf("%s",s1[ans[len1-1][len2-1][i]]);
    printf("\n");
}

int main()
{
    while(scanf("%s",&s1[1])!=-1){
        len1=2;len2=1;
        while(scanf("%s",&s1[len1])&&strcmp(s1[len1],"#"))
            len1++;
        while(scanf("%s",&s2[len2])&&strcmp(s2[len2],"#"))
            len2++;
        dp();
        print();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值