01 交换串 题解
题意简述
给定一个长为 \(n\) 的 \(\tt 01\) 串 \(s\),你需要对 \(s\) 进行恰好 \(m\) 次交换,每次只能交换相邻的不同字符,最大化操作后 \(s\) 的价值。\(s\) 的价值被定义为,对于每一个 \(i\),记 \(1 \sim i - 1\) 中,和 \(s_i\) 不同的 \(s_j\) 有 \(l\) 个,\(r\) 同理,\(f(s_i, i, l, r)\) 的和就是 \(s\) 的价值。
对于原题,是最大化得到的串中子序列 \(\tt 0010\) 和 \(\tt 1101\) 的总数。其实,此时 \(f(s_i, i, l, r) = \dfrac{l(l-1)}{2} r\)。我们的 \(f\) 完全可以更复杂,比如加入 \((-1) ^ {s_i} v_i\) 的系数,\(l^{i - 1} r^{n - i}\) 等奇怪的东西。
\(n, m \leq 500\),保证 \(s\) 可以操作。
题目分析
首先,根据大名鼎鼎的「\(\tt 01\) 守恒定理」,\(\tt 01\) 不会凭空产生,也不会凭空消失,操作前后 \(s\) 中 \(0 / 1\) 的数量没有发生变化。
我们进而发现,我们只会交换不同的字符,那么相同的字符之间的相对位置关系也是确定了的。这样,我们可以计算到达最终的字符串需要进行多少步,就是每一个 \(0\) 或每一个 \(1\),对应后位置之差绝对值之和。
我们考虑 DP,记 \(f'_{i, j, k}\) 表示最终字符串,已经决策前 \(i\) 位,前 \(i\) 位中有 \(j\) 个 \(0\),\(i - j\) 个 \(1\),\(0\) 的位置之差绝对值之和为 \(k\),的 \(\max \sum f\)。转移时,按照 \(s_i = 0 / 1\) 分类转移即可。其实分析到这里,代码就很好敲了。
记 \(0 / 1\) 出现次数为 \(c_{0 / 1}\),原串中每个 \(1\) 出现的位置序列为 \(p\),转移方程:
其中 \(a \gets b\) 表示 \(a \gets \max \{ a, b \}\)。
最终答案是 \(f'_{n,\ c_0,\ m}\) 吗?我们可以交换两次相邻不同的位置,浪费 \(2k\) 次操作,所以答案为 \(\max \limits _ {k = 0} ^ {\left\lfloor \dfrac{m}{2} \right\rfloor} f'_{n,\ c_0,\ m - 2k}\)。
代码
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 520;
int n, m;
char str[N];
int $0[N], $1;
int f[2][N][N], ans;
inline void tomax(int &a, int b) { b > a && (a = b); }
signed main() {
#ifndef XuYueming
freopen("b6e6.in", "r", stdin);
freopen("b6e6.out", "w", stdout);
#endif
scanf("%s%d", str + 1, &m);
for (; str[n + 1]; ++n);
for (int i = 1; i <= n; ++i) {
if (str[i] == '0') $0[++*$0] = i;
else ++$1;
}
memset(f[0], 0xff, sizeof (f[0]));
f[0][0][0] = 0;
for (int i = 0, t; i <= n - 1; ++i) {
memset(f[(i + 1) & 1], 0xff, sizeof (f[(i + 1) & 1]));
for (int j = max(0, i - $1); j <= i && j <= *$0; ++j)
for (int k = 0; k <= m; ++k)
if (~f[i & 1][j][k]) {
tomax(f[(i + 1) & 1][j][k], f[i & 1][j][k] + (j * (j - 1) >> 1) * (*$0 - j));
if (j == *$0) continue;
t = k + abs($0[j + 1] - i - 1);
if (t <= m)
tomax(f[(i + 1) & 1][j + 1][t], f[i & 1][j][k] + ((i - j) * (i - j - 1) >> 1) * ($1 - (i - j)));
}
}
for (int i = m; i >= 0; i -= 2)
ans = max(ans, f[n & 1][*$0][i]);
printf("%d", ans);
return 0;
}
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18444469。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。