colors颜色的长度 (来自刘汝佳紫书) dp+dp+dp

来源:互联网 发布:不败传说冰川网络 编辑:程序博客网 时间:2024/05/17 22:26

【问题描述】

  输入两个颜色序列(只包含大写字母的字符串),要求按顺序合并成同一个序列,即每次可以把一个序列的开头的颜色放到新序列的尾部。

  例如,两个颜色序列GBBY和YRRGB,至少有两种合并结果:GBYBRYRGB和YRRGGBBYB。对于每个颜色c来说,其跨度L(c)等于最大位置和最小位置之差,例如对于上面两种合并结果,每个颜色的L(c)和所有L(C)总和如下表:
    
  你的任务是找一种合并方式,使得所有L(c)的总和最小。

【输入格式】

  输入包含两行,每行一个只含大写字母的字符串,表示两个颜色序列。

【输出格式】

  输出一个整数,表示L(c)总和的最小值。

【输入样例】

【样例1】
 AAABBCY
 ABBBCDEEY

【样例2】
 GBBY
 YRRGB

【输出样例】

【样例1】
 10

【样例2】
 12

【数据范围】

颜色序列的长度不超过5000。

————————————————————————————————————————————————————————

这个。。。考试的时候我看出来是dp但是没有写出方程。。。
思路和那个擦数游戏很像,都是属于每对一个元素操作的时候会对其他元素的权值造成影响,求所有权值和的最优值的问题。这个时候要计算出这个影响。

f(i,j)表示现在选了A的前i个和B的前j个可以得到的最小L。
f(i,j)=min( f(i-1,j)+f(i,j-1) ) + g(i,j)
注意初始化!!!!
如果现在选择一个字符,那么剩下的所有没有选完的字母对应的L都要加一。
g(i,j)表示A选到第i个,B选到第j个,没有选的字母和已经选了的字母中都有的不同字母的数量。
g用lena*lenb的时间递推。
总时间复杂度为O(lena*lenb)
la[i][j]表示A从左到第i个的时候左边颜色j的个数
ra[i][j]表示A从右到第i个的时候右边颜色j的个数
g(i,j)=g(i,j-1)+( la[i][B[j]-‘A’]+lb[j-1][B[j]-‘A’]==0 ? 1 : 0 ); <-新颜色,++
if(ra[i+1][B[j]-‘A’]+rb[j+1][B[j]-‘A’]==0) g(i,j)–; <-之后这个颜色没有了,不满足两边都有,–
g(i,0)=g(i-1,0)+(la[i-1][A[i]-‘A’]==0 ? 1 : 0);
if(ra[i+1][A[i]-‘A’]+rb[1][A[i]-‘A’]==0) g(i,0)–;
g(0,j)类似。

当然还有其他版本的写法,这里给出比较容易理解的(或者说是我自己写的嗯)。

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>#include<ctime>#define inf 1e9using namespace std;const int maxn=5005;char A[maxn],B[maxn];int N,M;int f[2][maxn],g[maxn][maxn];int la[maxn][30],lb[maxn][30],ra[maxn][30],rb[maxn][30];void data_in(){    gets(A+1);gets(B+1);    N=strlen(A+1);    M=strlen(B+1);}void dp1(){    for(int i=1;i<=N;i++)    for(int j=0;j<26;j++)        la[i][j]=la[i-1][j]+(A[i]-'A'==j ? 1 : 0);    for(int i=1;i<=M;i++)    for(int j=0;j<26;j++)        lb[i][j]=lb[i-1][j]+(B[i]-'A'==j ? 1 : 0);    for(int i=N;i>=1;i--)    for(int j=0;j<26;j++)        ra[i][j]=ra[i+1][j]+(A[i]-'A'==j ? 1 : 0);    for(int i=M;i>=1;i--)    for(int j=0;j<26;j++)        rb[i][j]=rb[i+1][j]+(B[i]-'A'==j ? 1 : 0);}void dp2(){    for(int i=1;i<=N;i++)    {        g[i][0]=g[i-1][0];        if(la[i-1][A[i]-'A']==0) g[i][0]++;        if(ra[i+1][A[i]-'A']+rb[1][A[i]-'A']==0) g[i][0]--;    }    for(int j=1;j<=M;j++)    {        g[0][j]=g[0][j-1];        if(lb[j-1][B[j]-'A']==0) g[0][j]++;        if(ra[1][B[j]-'A']+rb[j+1][B[j]-'A']==0) g[0][j]--;    }    for(int i=1;i<=N;i++)    for(int j=1;j<=M;j++)    {        g[i][j]=g[i][j-1];        if(la[i][B[j]-'A']+lb[j-1][B[j]-'A']==0) g[i][j]++;        if(ra[i+1][B[j]-'A']+rb[j+1][B[j]-'A']==0) g[i][j]--;    }}void dp3(){    for(int i=1;i<=N;i++) f[i][0]=f[i-1][0]+g[i][0];    for(int j=1;j<=M;j++) f[0][j]=f[0][j-1]+g[0][j];    for(int i=1;i<=N;i++)    for(int j=1;j<=M;j++)        f[i][j]=min(f[i-1][j],f[i][j-1])+g[i][j];}void work(){    dp1();    dp2();    dp3();    printf("%d\n",f[N][M]);}int main(){    freopen("color.in","r",stdin);    freopen("color.out","w",stdout);    data_in();    work();    return 0;}
原创粉丝点击