[UVA1437] String painter

题目链接

题意

  有两个由小写英文字母组成的等长字符串 $A$ 和 $B$。你可以一次性将一个字符串的一个子串中的字符全部刷成任何你想要同一字符。求把字符串 $A$ 刷成 $B$ 的最少次数。

分析

  这一题非常像 [CQOI2007] 涂色 ,准确说是加强版

  但是简单考虑直接将 $A$ 刷成 $B$,由于 $AB$ 两串中字符的不确定性(有些位置上字符相同而有些不同),状态转移方程比较难推出,所以可以换一种思路来简化问题

  我们可以分两步 $DP$ 处理,先求出将空白串刷成 $B$ 串中任意子串的次数,再借此得出 $A$ 刷成 $B$ 的结果

  定义二维数组 $f[i][j]$ 表示将空白串的 $i$ 到 $j$ 位置刷成 $B$ 串的 $i$ 到 $j$ 位置的最少次数

  当 $i=j$ 时,$f[i][j]=1$

  当 $i \neq j$ 时,

$$f[i][j] = \begin{cases} min(f[i+1][j],f[i][j-1]) & B[i]=B[j] \\ min(f[i][j],f[i][k]+f[k+1][j])(i \leq k < j) & B[i] \neq B[j] \end{cases}$$

  显然需要区间DP,不断扩展枚举子串的长度,并且要枚举子串中间断点k

  然后定义d[i]表示将A串前i个字符刷成B串前i个字符的最少次数,最初 $d[i]=f[1][i]$

$$d[i] = \begin{cases} min(d[i],d[i-1]) & A[i]=B[j] \\ min(d[i],d[k]+f[k+1][i])(i \leq k < j) & A[i] \neq B[j] \end{cases}$$

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define ll long long
#define inf 0x7fffffff
#define N 105

int n;
char a[N], b[N];
int f[N][N], d[N];

int main() {
    while (scanf("%s%s", a + 1, b + 1) == 2) {
        memset(f, 0x3f, sizeof f);
        n = strlen(a + 1);
        for (int i = 1; i <= n; i++) f[i][i] = 1;
        for (int l = 2; l <= n; l++)
            for (int i = 1; i + l - 1 <= n; i++) {
                int j = i + l - 1;
                if (b[i] == b[j])
                    f[i][j] = min(f[i + 1][j], f[i][j - 1]);
                else for (int k = i; k < j; k++)
                    f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
            }
        for (int i = 1; i <= n; i++) {
            d[i] = f[1][i];
            if (a[i] == b[i])
                d[i] = min(d[i], d[i - 1]);
            else for (int k = 1; k < i; k++)
                d[i] = min(d[i], d[k] + f[k + 1][i]);
        }
        printf("%d\n", d[n]);
    }

    return 0;
}
View Code
posted @ 2019-06-02 17:19  Pedesis  阅读(149)  评论(0编辑  收藏  举报