01 交换串 题解

题意简述

给定一个长为 n01s,你需要对 s 进行恰好 m 次交换,每次只能交换相邻的不同字符,最大化操作后 s 的价值。s 的价值被定义为,对于每一个 i,记 1i1 中,和 si 不同的 sjl 个,r 同理,f(si,i,l,r) 的和就是 s 的价值。

对于原题,是最大化得到的串中子序列 00101101 的总数。其实,此时 f(si,i,l,r)=l(l1)2r。我们的 f 完全可以更复杂,比如加入 (1)sivi 的系数,li1rni 等奇怪的东西。

n,m500,保证 s 可以操作。

题目分析

首先,根据大名鼎鼎的「01 守恒定理」,01 不会凭空产生,也不会凭空消失,操作前后 s0/1 的数量没有发生变化。

我们进而发现,我们只会交换不同的字符,那么相同的字符之间的相对位置关系也是确定了的。这样,我们可以计算到达最终的字符串需要进行多少步,就是每一个 0 或每一个 1,对应后位置之差绝对值之和。

我们考虑 DP,记 fi,j,k 表示最终字符串,已经决策前 i 位,前 i 位中有 j0ij10 的位置之差绝对值之和为 k,的 maxf。转移时,按照 si=0/1 分类转移即可。其实分析到这里,代码就很好敲了。

0/1 出现次数为 c0/1,原串中每个 1 出现的位置序列为 p,转移方程:

fi+1, j, kfi, j, k+f(1,i+1,j,c0j)fi+1, j+1, k+|pj+1(i+1)|fi, j, k+f(0,i+1,ij,c1(ij))

其中 ab 表示 amax{a,b}

最终答案是 fn, c0, m 吗?我们可以交换两次相邻不同的位置,浪费 2k 次操作,所以答案为 maxk=0m2fn, c0, m2k

代码

#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;
}
posted @   XuYueming  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示