Loading

Lazy Narek

算法

先转化题意

\(n\) 个长为 \(m\) 的字符串

假设按顺序找到 \(\rm{n, a, r, e, k}\) 这几个字符的数量为 \(q\) , \(\rm{n, a, r, e, k}\) 出现的总数量为 \(p\)

求最优的拼接方法, 使得 \(q - (p - q) = 2q - p \to max\)

\(f_{i, 0/1}\) 表示考虑了前 \(i\) 个字符串, 是否选择第 \(i\) 个字符串, 然而这样并不好设计

注意到 \(0/1\) 维是无必要的, 我们考虑记录 \(f_{i, s}\) 表示考虑了前 \(i\) 个字符串, 其中最后以字母 \(s \in \{\rm{n, a, r, e, k}\}\) 结尾时的最优分差

显然的, 我们可以预处理出每一个字符串以 \(s \in \{\rm{'n', 'a', 'r', 'e', 'k'}\}\) 结尾时最远的可取字符和产生的分差, 记为 \(end_{i, s}\)\(val_{i, s}\) , 特别的, 以 \(\rm{'k'}\) 结尾相当于从 \(\rm{'n'}\) 重新开始计算, 预处理时间复杂度 \(\mathcal{O}(\omega^2 nm)\) , 其中 \(\omega = 5\)

考虑转移

\[\begin{cases} f_{i, end_{i, s}} \stackrel{\max}{\longleftarrow} f_{i - 1, s} + val_{i, s} \\ f_{i, s} \stackrel{\max}{\longleftarrow} f_{i - 1, s} \end{cases} \]

复杂度 \(\mathcal{O}(\omega^2 nm)\) , 其中 \(\omega = 5\)

初始化 \(f_{0, s = \rm{'k'}} = 0\) , 其他都为负无穷
答案即为 \(\displaystyle\max_{s \in \{\rm{n, a, r, e, k}\}} f_{n, s}\)

代码

#include <bits/stdc++.h>
const int MAXN = 1e3 + 20;

int T;
int n, m;
std::string str[MAXN];

char mp[5] = {'n', 'a', 'r', 'e', 'k'};
int end[MAXN][5], val[MAXN][5];
/*预处理*/
void init()
{
    /*预处理 end 数组*/
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 5; j++) {
            char NowStart = (j == 4 ? -1 : j);
            for (int k = 1; k <= m; k++) {
                if (str[i][k] == mp[NowStart + 1]) NowStart = ((NowStart + 1) == 4 ? -1 : NowStart + 1);
            }

            end[i][j] = (NowStart == -1 ? 4 : NowStart);
        }

    /*预处理 val 数组*/
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 5; j++) {
            int Sum = 0; // 总出现的次数
            int Use = 0; // 可使用的次数
            char NowStart = (j == 4 ? -1 : j);
            for (int k = 1; k <= m; k++) {
                for (int w = 0; w < 5; w++) if (str[i][k] == mp[w]) Sum++;
                if (str[i][k] == mp[NowStart + 1]) NowStart = ((NowStart + 1) == 4 ? -1 : NowStart + 1), Use += (NowStart == -1 ? 5 : 0);
            }

            val[i][j] = 2 * Use - Sum;
        }
}

int dp[MAXN][5];
/*dp 转移*/
void solve()
{
    memset(dp, 128, sizeof(dp));
    dp[0][4] = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 5; j++) {
            dp[i + 1][j] = std::max(dp[i + 1][j], dp[i][j]);
            dp[i + 1][end[i + 1][j]] = std::max(dp[i + 1][end[i + 1][j]], dp[i][j] + val[i + 1][j]);
        }
    }

    int Ans = 0;
    for (int j = 0; j < 5; j++) {
        Ans = std::max(Ans, dp[n][j]);
    }
    printf("%d\n", Ans);
}

int main()
{
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= n; i++) {
            std::cin >> str[i];
            str[i] = ' ' + str[i];
        }

        init();
        solve();
    }

    return 0;
}

总结

分析不必要的维度, 考虑简化

一定要设计特征状态

预处理可以降低复杂度

posted @ 2024-11-30 08:54  Yorg  阅读(14)  评论(0)    收藏  举报