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;
}
总结
分析不必要的维度, 考虑简化
一定要设计特征状态
预处理可以降低复杂度

浙公网安备 33010602011771号