Color Length UVA - 1625 (DP)

题意:

链接:https://vjudge.net/problem/UVA-1625

给你两个由大写字母组成的序列,让你把这两个序列按这两个序列的原序列的字母顺序拼接成一个新的串,让你输出这个新的串的距离的最小值(这里的距离的最小值的定义是:相同字母的最远距离之和 如: ACACA  -> 距离为 4(A与A的最远距离) + 2(C与C的最远距离) = 6  )

解题思路:

这个dp转移方程挺容易想的,设这两个序列分别为a串和b串,那么当前的状态 dp[ i ][ j ] 代表 a 串使用了前 i 个, b 串使用了前  j 个的最小总的距离,当前状态可以由 a 串使用了前 i- 1 个, b 串使用了前  j 个  或  a 串使用了前 i 个, b 串使用了前  j - 1 个 转移过来,转移到 dp[ i ][ j ]会多花费 x 距离,所以 : dp[ i ][ j ] = min( dp[i - 1][ j ], dp[ i ][ j - 1 ] ) + x  
那么x怎么求,可以这么理解,每次添加一个字母到新串所增加的距离其实就是 : 有多少个字母已经出现但是还未结束。
所以先对这两个串进行预处理,求出每个串中每个字母的开始和结束,这样就可以方便一些的求出有多少个字母已经出现但是还未结束。就是遍历 26 个字母,如果该字母 已经 出现过 但是没结束  ans++ 。具体细节参考代码。

AC代码:

#include<bits/stdc++.h>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false),cin.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 5000 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

char t[maxn];
int dp[maxn][maxn];
int a[30], b[30];
int sa[30], ta[30];
int sb[30], tb[30];
int len_a, len_b;

inline void init()
{

    up(i, 1, 26) sa[i] = inf;
    up(i, 1, 26) ta[i] = -inf;
    up(i, 1, 26) sb[i] = inf;
    up(i, 1, 26) tb[i] = -inf;

    up(i, 1, len_a)
    {
        sa[a[i]] = min(sa[ a[i] ], i);  // 最早开始位置
        ta[a[i]] = i;                   // 结束位置
    }
    up(i, 1, len_b)
    {
        sb[b[i]] = min(sb[ b[i] ], i);  // 最早开始位置
        tb[b[i]] = i;                   // 结束位置
    }
}

inline int get(int i, int j)  // 经常使用get,所以加了下内联,常数小一点
{
    int ans = 0;
    for(int k = 1; k <= 26; k++)
    {
        if(sa[k] == inf && sb[k] == inf) continue;  //如过不存在该字母就不用管
        int f1 = 0, f2 = 0;
        if(i >= sa[k] || j >= sb[k]) f1 = 1;  // 该字母已经开始
        if(i < ta[k] || j < tb[k]) f2 = 1;    // 该字母还未结束
        if(f1 && f2) ans++;  //如果已经开始还未结束就 ++
    }
    return ans;
}

int main()
{
    int T; scanf("%d", &T); while(T--)
    {
        scanf("%s", t + 1); len_a = strlen(t + 1);
        up(i, 1, len_a) a[i] = t[i] - 'A' + 1;
        scanf("%s", t + 1); len_b = strlen(t + 1);
        up(i, 1, len_b) b[i] = t[i] - 'A' + 1;

        init();
        
        up(i, 0, len_a) up(j, 0, len_b) dp[i][j] = inf;
        dp[0][0] = 0;
        up(i, 1, len_a) dp[i][0] = dp[i - 1][0] + get(i, 0); 
        up(j, 1, len_b) dp[0][j] = dp[0][j - 1] + get(0, j);

        for(int i = 1; i <= len_a; i++)
        {
            for(int j = 1; j <= len_b; j++)
            {
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + get(i, j);
            }
        }
        printf("%d\n", dp[len_a][len_b]);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值