【DP模型:LCS】uva1625 Color Length

题目描述:对给定的两个长度为n和m的颜色序列,将他们合并,即每次把序列的开头放入新的序列的尾部。计每种颜色的跨度为序列中最大位置与最小位置之差。求最小跨度。
数据范围:n,m<=5000

这道题很容易想到LCS(最长公共子序列)。然而并没有什么用= =
一开始我想的状态转移方程式:
f[i][j]=max(f[i-1][j]+add(a[i]),f[i][j-1]+add(b[j])) //a,b为两个颜色序列 add表示放入某元素增加的跨度
发现add()并不好算= =

看大神的方法:采用倒推。
令f(i,j)表示两个串的下标移动至i,j时,还要产生的最小跨度。
方程式:f(i,j)=min(f(i,j+1),f(i+1,j))+add(i,j)
初始化:f(lena,lenb)=0
其中add(i,j)怎么算?
计b1[i]为i在第一个序列开始位置,b2[i]为i在第二个序列开始位置
e1[i]为i在第一个序列结束位置,e2[i]为i在第二个序列结束位置
若i已经出现完了,就不再增加跨度。若i还未出现,也不再增加跨度。其余情况均增加跨度。(具体操作见代码)
目标:f(0,0)

在实现过程中注意细节。
本题思维难度适中。然而我并没有想出来= = 说明逆向思维不够= =
蒟蒻加油 ↖(^ω^)↗

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define MAXN 5005
using namespace std;

int len1 ,len2 ,b1[27] ,b2[27] ,e1[27] ,e2[27] ;
int f[MAXN][MAXN] ;
char c1[MAXN] ,c2[MAXN] ;

int add(int a,int b)
{
    --a,--b;
    int cnt=0;
    for(int i=0;i<26;++i)
    {
        if(e1[i]<=a&&e2[i]<=b)continue;
        if(b1[i]>a&&b2[i]>b||b1[i]>a&&b2[i]==-1||b2[i]>b&&b1[i]==-1)continue;
        ++cnt;
    }
    return cnt;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(b1,-1,sizeof(b1));
        memset(b2,-1,sizeof(b2));
        memset(e1,-1,sizeof(e1));
        memset(e2,-1,sizeof(e2));

        scanf("%s%s",c1,c2);
        len1=strlen(c1),len2=strlen(c2);

        for(int i=0;i<len1;++i)
        {
            c1[i]-='A';
            if(b1[c1[i]]==-1)
                b1[c1[i]]=i;
            e1[c1[i]]=i;
        }
        for(int i=0;i<len2;++i)
        {
            c2[i]-='A';
            if(b2[c2[i]]==-1)
                b2[c2[i]]=i;
            e2[c2[i]]=i;
        }

        f[len1][len2]=0;
        for(int i=len2-1;i>=0;--i)
            f[len1][i]=f[len1][i+1]+add(len1,i);
        for(int i=len1-1;i>=0;--i)
        {
            f[i][len2]=f[i+1][len2]+add(i,len2);
            for(int j=len2-1;j>=0;--j)
                f[i][j]=min(f[i][j+1],f[i+1][j])+add(i,j);
        }
        printf("%d\n",f[0][0]);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值