UVa 1625 颜色的长度(dp)

题意:

输入两个长度分别为n和m的颜色序列,要求按顺序合并成同一个序列,即每次可以把一个序列开头的颜色放到新序列的尾部。对于每个颜色c来说,其跨度L(c)等于最大位置和最小位置之差。

解析:
比较难的一道dp,反正我是想了好久都想不出来,这里参考了网上的思路,具体看代码注释

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
#define pll pair<ll,ll>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep1(i,a,b) for(int i=a;i>=b;i--)
#define rson rt<<1|1,m+1,r
#define lson rt<<1,l,m
using namespace std;
const int N=5e3+100,M=27;
/*
    c[i][j]表示在字符串1中移走了i个字符
    在字符串2中移走了j个字符
    在主字符串中已经开始还未结束的字符个数
    dp[i][j]表示两个序列已经分别移走了i和j个元素时的最小代价。
    s1,e1数组分别用来表示序列1中每个字母的开头位置和结束位置
    s2,e2分别用来表示序列1中每个字母的开头位置和结束位置。
    新增一个字符后,所有已经出现的但没有结束的字符的跨度L(c)都要+1
    则状态转移方程为
    dp(i,j)=min(dp(i-1,j)+c[i-1][j],dp(i,j-1)+c[i][j-1])。

*/
int c[N][N],s1[M],e1[M],s2[M],e2[M],dp[N][N];
char ch1[N],ch2[N];
int main()
{
    int t,n,m ;
    cin>>t;
    while(t--)
    {
        string str1,str2;
        cin>>str1>>str2;
        n=str1.size(),m=str2.size();
        for(int i=n;i>0;i--)
            str1[i]=str1[i-1];
        for(int i=m;i>0;i--)
            str2[i]=str2[i-1];
        memset(s1,inf,sizeof s1);
        memset(s2,inf,sizeof s2);
        memset(e1,0,sizeof e1);
        memset(e2,0,sizeof e2);
        /*注意:这样的初始化是方便c数组的求解
        不同的初始化c数组的求解方式也不同*/
        for(int i=1;i<=n;i++)
        {
            int a=(int)str1[i]-'A';
            s1[a]=min(s1[a],i);
            e1[a]=i;
        }
        for(int j=1;j<=m;j++)
        {
            int a=(int)str2[j]-'A';
            s2[a]=min(s2[a],j);
            e2[a]=j;
        }
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=m;j++)
            {
                int a=(int)str1[i]-'A';
                int b=(int)str2[j]-'A';
                if(i)
                {
                    c[i][j]=c[i-1][j];
                    if(s1[a]==i&&s2[a]>j) c[i][j]++;//新出现的字符
                    if(e1[a]==i&&e2[a]<=j) c[i][j]--;//要结束的字符
                }
                if(j)
                {
                    c[i][j]=c[i][j-1];
                    if(s2[b]==j&&s1[b]>i) c[i][j]++;
                    if(e2[b]==j&&e1[b]<=i) c[i][j]--;
                }
            }
        }
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=m;j++)
            {
                if(!i&&!j) continue;
                int v1=inf,v2=inf;
                if(i) v1=dp[i-1][j]+c[i-1][j];
                if(j) v2=dp[i][j-1]+c[i][j-1];
                dp[i][j]=min(v1,v2);
            }
        }
        cout<<dp[n][m]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ffgcc/article/details/80237375