zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    BZOJ 3132: 上帝造题的七分钟 树状数组+差分
    PAT Advanced 1006 Sign In and Sign Out (25 分)
    PAT Advanced 1011 World Cup Betting (20 分)
    PAT Basic 1032 挖掘机技术哪家强 (20 分)
    PAT Basic 1028 人口普查 (20 分)
    PAT Basic 1004 成绩排名 (20 分)
    大数据数据库HBase(二)——搭建与JavaAPI
    PAT Advanced 1009 Product of Polynomials (25 分)(vector删除元素用的是erase)
    PAT Advanced 1002 A+B for Polynomials (25 分)(隐藏条件,多项式的系数不能为0)
    PAT Basic 1041 考试座位号 (15 分)
  • 原文地址:https://www.cnblogs.com/quanjun/p/13892931.html
Copyright © 2011-2022 走看看