UVA1625[Color Length] 序列动态规划模型

题目链接


题意: 题目的意思是,给你两个长度分别为n和m的颜色序列(n,m<=5000)

都是由大写字母组成,要求按照顺序合并成同一个序列,即每次

可以把一个序列开头的颜色放在新序列的尾部

比如两个序列:GGBY 和 YRRGB至少有两种合并结果:

GBYBRYRGB 和 YRRGGBBYB 对于每一种颜色c,跨度L(c)是最大位置和

最小位置之差,

问题是: 找一种合并的方式使得L(c)的总和最小。


解题报告:

对于序列动态规划,我们常常用 dp[i][j] 来表示 第一个串1~i,第二个串1~j 的答案。

对于这道题,我们用类似的方法,用 dp[i][j] 来表示, 第一个串的前 i个元素, 第二个串的前j个元素,已经合并入队列的最小代价。 但是怎么转移呢,我们发现由于序列一直在变化,所以不好转移每个字母的L(C)。 lrj的书上写了一个巧妙的方法, 那就是如果一个字母没有结束(以后还可以向队列加入这种字母),那么它的L(c)值就还可以增加,因此我们不必等一个字母结束后在来计算,而可以在过程中计算。为了快速转移,我们增加一个数组w[i][j], 表示当第一个串前i个元素,第二个串前j个元素已经加入到队列中时,有w[i][j]种字母还没有结束。

dp[i][j]=min(dp[i-1][j]+w[i-1][j],dp[i][j-1]+w[i][j-1])

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 5e3 + 7;
const int M = 30;
int dp[N], w[N][N], d[N][N];
int sA[M], eA[M], sB[M], eB[M];
char A[N], B[N];
int a[N], b[N];
int main(){
    int T;
    scanf("%d", &T );
    while( T-- ){
        scanf("%s%s", A+1, B+1); 
        int lenA=strlen(A+1), lenB=strlen(B+1);
        for ( int i=1; i<=29; i++ ) sA[i]=sB[i]=eA[i]=eB[i]=0; 
        for ( int i=1; i<=lenA; i++ ) {
            a[i]=A[i]-'A'+1;
            if( !sA[a[i]] ) sA[a[i]]=i;
            eA[a[i]]=i;
        }
        for ( int i=1; i<=lenB; i++ ) {
            b[i]=B[i]-'A'+1;
            if( !sB[b[i]] ) sB[ b[i] ]=i;
            eB[ b[i] ]=i;
        } 
        for ( int i=1; i<=27; i++ ) if( !sA[i] ) sA[i]=lenA+1;
        for ( int i=1; i<=27; i++ ) if( !sB[i] ) sB[i]=lenB+1; 
        for ( int i=0; i<=lenA; i++ ){
            for ( int j=0; j<=lenB; j++ ) {
                w[i][j]=0;
                if (i){
                    int x1=w[i-1][j];
                    if( i==eA[ a[i] ] && j>=eB[ a[i] ]  ) x1--;
                    if( i==sA[ a[i] ] && j<sB[ a[i] ] ) x1++;   
                    w[i][j]=max(w[i][j],x1);
                } 
                if (j){
                    int x2=w[i][j-1];
                    if( j==eB[ b[j] ] && i>=eA[ b[j] ] ) x2--;
                    if( j==sB[ b[j] ] && i<sA[ b[j] ] ) x2++;   
                    w[i][j]=max(w[i][j],x2);
                }
            }
        }

        for ( int i=1; i<=lenA; i++ )
            for ( int j=1; j<=lenB; j++ )
                dp[j]=min(dp[j]+w[i-1][j],dp[j-1]+w[i][j-1]);
        for ( int i=0; i<=lenA; i++ )
            for ( int j=0; j<=lenB; j++ ){
                if( !i&&!j ) continue;
                d[i][j]=(1<<21)-1;
                if( i ) d[i][j]=min(d[i][j],d[i-1][j]+w[i-1][j]);
                if( j ) d[i][j]=min(d[i][j],d[i][j-1]+w[i][j-1]);
            }
        printf("%d\n", d[lenA][lenB] );
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值