CF771D Bear and Company 题解 动态规划

题目链接:https://codeforces.com/problemset/problem/771/D

题目大意:给你一个长度为 (n(1 le n le 75)) 的字符串,每一步操作你可以交换字符串中相邻的两个元素。求最少的操作步数,使得字符串中不包含子串 “VK”。

解题思路:

因为除了 'V' 和 'K' 以外的所有字符的效果都是一样的,所以我们可以把其它的字符都视为 'X'。

我们会从左往右一步步构造出最终的字符串。令 (dp[v][k][x]) 表示构造一个包含 (v) 个字符 'V',(k) 个字符 'K' 以及 (x) 个字符 'X' 的字符串前缀所需要的操作次数(这个字符串前缀对应的长度为 (v+k+v))。但是这个状态是不完整的,我们还需要开一个维度来确保 'V' 和 'K' 有没有连接到一起(确保不存在 'V' 后面紧跟着一个 'K' 的情况出现)。所以我们需要再开一个维度来确保这个长度为 (v+k+x) 的字符串前缀的最后一个字符是不是 'V'。于是,最终定义的状态为 (dp[v][k][x][lastLetter])(或者可以表述成 (dp[v][k][x][is\_the\_last\_letter\_V])(Rightarrow) 它表示构造一个包含 (v) 个字符 'V'、(k) 个字符 'K'、(x) 个字符 'X' 且最后一个字符是不是 'V' 的字符串前缀需要的最少交换次数。

要从当前状态转移到下一个状态,我们需要考虑下一个 'K'(也就是初始状态下的第 (k+1) 个字符 'K')的位置,下一个 'V' 个位置,以及下一个 'X' 的位置。当然,如果当前状态的最后一个字符是 'V',我们的下一个状态的最后一个字符不能是 'K'。

也就是说,如果当前状态是 (dp[v][k][x][i]) (当前有字符串前缀有 (v) 个字符 'V',(k) 个字符 (K)(x) 个字符 'X',(i=1) 表示前缀的最后一个字符是 'V',(i=0) 表示前缀的最后一个字符不是 'V')的话,则它可以扩展出如下状态(假设字符 'V' 的总数为 (V),字符 'K' 的总数为 (K),其他字符(视为 'X')的总数为 (X)):

  • 如果 (v lt V),则可以扩展到状态 (dp[v+1][k][x][1])(将第 (v+1) 个 'V' 移动到第 (v+k+x+1) 个位置);
  • 如果 (k lt K),则可以扩展到状态 (dp[v][k+1][x][0])(将第 (k+1) 个 'K' 移动到第 (v+k+x+1) 个位置);
  • 如果 (x lt X),则可以扩展到状态 (dp[v][k][x+1][0])(将第 (x+1) 个 'X' 移动到第 (v+k+x+1) 个位置)。

那么将一个字符转移到第 (v+k+x+1) 个位置需要交换几次呢?

可以发现,之前的状态是 (dp[v][k][x][i]),所以前 (v) 个 'V'、前 (k) 个 'K'、前 (x) 个 'X' 的位置我们不用管,假设要转移的字符一开始在第 (p) 个位置,则它对应的交换次数是:

(v+1) 个 'V' 开始所有初始位置 (lt p) 的字符 'V' 个数 + 第 (k+1) 个 'K' 开始所有初始位置 (lt p) 的字符 'K' 个数 + 第 (x+1) 个 'X' 开始所有初始位置 (lt p) 的字符个数 之和。

这就是转移的代价。

计算转移的代价可以有更快地解法,不过我们就算以 (O(n)) 的算法解决也可以过,那么遍历 'V'、'K'、'X' 以及计算转移的代价,总时间复杂度 (O(n^4)) 可以解决这个问题。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 77;
int n, dp[maxn][maxn][maxn][2], V, K, X;
char s[maxn];
vector<int> vec_v, vec_k, vec_x;

int cal_swap_time(int v, int k, int x, int p) {
    int cnt = 0;
    for (int i = v; i < V && vec_v[i] < p; i ++) cnt ++;
    for (int i = k; i < K && vec_k[i] < p; i ++) cnt ++;
    for (int i = x; i < X && vec_x[i] < p; i ++) cnt ++;
    return cnt;
}

void chkmin(int &a, int b) {
    a = min(a, b);
}

int main() {
    scanf("%d%s", &n, s+1);
    for (int i = 1; i <= n; i ++) {
        if (s[i] == 'V') vec_v.push_back(i);
        else if (s[i] == 'K') vec_k.push_back(i);
        else vec_x.push_back(i);
    }
    V = vec_v.size();
    K = vec_k.size();
    X = vec_x.size();
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0][0][0] = 0;
    for (int v = 0; v <= V; v ++) {
        for (int k = 0; k <= K; k ++) {
            for (int x = 0; x <= X; x ++) {
                for (int i = 0; i < 2; i ++) {
                    int tmp = dp[v][k][x][i];
                    if (v < V) chkmin(dp[v+1][k][x][1], tmp + cal_swap_time(v, k, x, vec_v[v]));
                    if (k < K && i==0) chkmin(dp[v][k+1][x][0], tmp + cal_swap_time(v, k, x, vec_k[k]));
                    if (x < X) chkmin(dp[v][k][x+1][0], tmp + cal_swap_time(v, k, x, vec_x[x]));
                }
            }
        }
    }
    printf("%d
", min(dp[V][K][X][0], dp[V][K][X][1]));
    return 0;
}
原文地址:https://www.cnblogs.com/quanjun/p/13892931.html