UVA 1625 - Color Length【DP】

题目大意

传送门

输入两个长度分别为n和m的颜色序列,要求按顺序合并为一个序列,要求每次把序列开头的颜色方法新序列的尾部。

现在有一个衡量标准L(c),L(c)表示的是颜色c的跨度:最大位置和最小位置之差。

对于字符串 GBBY 和字符串 YRRGB 来说:
当合并为 YRRGGBBYB 的时候,其L(c)分别为

colorGYBRSum
L(c)173112

思路

使用dp[i][j]表示从S1中取出前i个字符,以及从S2中取出前j个字符,组成字符串,这个字符串L(c)的sum值。
那么可以得到以下状态转移方程:

        for(int i = 0; i <= len1; i++)
        {
            for(int j = 0; j <= len2; j++)
            {
                //将S1[i]插入到dp[i-1][j]中
                if(i>0)
                    dp[i][j] = min(dp[i][j],dp[i-1][j] + cnt[i-1][j]);
                //将S2[j]插入到dp[i][j-1]中
                if(j>0)
                    dp[i][j] = min(dp[i][j],dp[i][j-1] + cnt[i][j-1]);
            }
        }

cnt[i][j]表示从字符S1中取出前i个字符,从字符S2中取出前j个字符,组成的字符串中,已经出现但是没有结束的字母的个数

        //cnt[i][j]字符串S1[i] + S2[j] 中已经开始但是没有结束的字母的个数
        for(int i = 0; i<= len1; i++)
        {
            for(int j = 0; j <= len2; j++)
            {
                for(int c = 0; c < 26; c++)
                {
                    if((c_list[c].l1 <= i || c_list[c].l2 <= j) && (c_list[c].r1 > i || c_list[c].r2 > j) ) cnt[i][j]++;
                }
            }
        }

代码


#include<stdio.h>
#include<cstring>
#include<set>
#define maxn 5005
#define INF 1<<30
#define min(a,b) (a>b)? b:a
#define max(a,b) (a>b)? a:b
using namespace std;

char S1[maxn];
char S2[maxn];

int dp[maxn][maxn];
int cnt[maxn][maxn];

struct Node
{
    int l1;//字符串S1中字母i的左边界
    int r1;//字符串S1中字母i的右边界

    int l2;//字符串S2中字母i的左边界
    int r2;//字符串S2中字母i的右边界
};

Node c_list[27];

int main ()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",S1);
        scanf("%s",S2);
        int len1 = strlen(S1);
        int len2 = strlen(S2);

        for(int i = 0; i<26; i++)
        {
            c_list[i].l1 = maxn+1;
            c_list[i].r1 = -1;

            c_list[i].l2 = maxn+1;
            c_list[i].r2 = -1;
        }

        for(int i = 0; i<len1; i++)
        {
            c_list[S1[i]-'A'].l1 = min(c_list[S1[i]-'A'].l1,i);
            c_list[S1[i]-'A'].r1 = max(c_list[S1[i]-'A'].r1,i);

        }
        for(int i = 0; i<len2; i++)
        {
            c_list[S2[i]-'A'].l2 = min(c_list[S2[i]-'A'].l2,i);
            c_list[S2[i]-'A'].r2 = max(c_list[S2[i]-'A'].r2,i);
        }

        for(int i = 0;i<26;i++){
            c_list[i].l1 +=1;
            c_list[i].r1 +=1;
            c_list[i].l2 +=1;
            c_list[i].r2 +=1;
            //printf("%c [%d %d] [%d %d]\n",i+'A',c_list[i].l1,c_list[i].r1,c_list[i].l2,c_list[i].r2);
        }

        for(int i = 0; i <= len1; i++)
        {
            for(int j = 0; j <= len2; j++)
            {
                dp[i][j] = INF;
                cnt[i][j] = 0;
            }
        }
        dp[1][0] = 0;
        dp[0][1] = 0;


        //cnt[i][j]字符串S1[i] + S2[j] 中已经开始但是没有结束的字母的个数
        for(int i = 0; i<= len1; i++)
        {
            for(int j = 0; j <= len2; j++)
            {
                for(int c = 0; c < 26; c++)
                {
                    if((c_list[c].l1 <= i || c_list[c].l2 <= j) && (c_list[c].r1 > i || c_list[c].r2 > j) ) cnt[i][j]++;
                }
            }
        }
/*
        for(int i = 0;i<=len1;i++){
            for(int j = 0; j <= len2; j++){
                    printf("%d\t",cnt[i][j]);
            }
            printf("\n");
        }
*/


        for(int i = 0; i <= len1; i++)
        {
            for(int j = 0; j <= len2; j++)
            {

                //将S1[i]插入到dp[i-1][j]中
                if(i>0)
                    dp[i][j] = min(dp[i][j],dp[i-1][j] + cnt[i-1][j]);


                //将S2[j]插入到dp[i][j-1]中
                if(j>0)
                    dp[i][j] = min(dp[i][j],dp[i][j-1] + cnt[i][j-1]);
            }
        }
/*
        printf("\n\n");
        for(int i = 0; i <= len1; i++){
            for(int j = 0; j <= len2; j++){
                    printf("%d\t",dp[i][j]);
            }
            printf("\n");
        }
*/

        printf("%d\n",dp[len1][len2]);
    }
}
/*

GBBY
YRRGB

*/

Hit

字符串相关的dp问题,最好是将字符串的起点设置为1,而不是0,这样会省去很多-1或者是+1的边界处理问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值