UVA 1625

来源:互联网 发布:windows字体怎么设置 编辑:程序博客网 时间:2024/04/30 18:56

  • 题目大意
  • 思路
  • 代码
  • Hit

题目大意

传送门

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

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

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

color G Y B R Sum L(c) 1 7 3 1 12

思路

使用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:busing 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]);    }}/*GBBYYRRGB*/

Hit

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