题目
题意:给定一个长度为n的二进制字符串,定义
f
(
s
)
f(s)
f(s)为字符串
s
s
s中所有相邻两位数的总和。即
d
i
=
s
i
s
i
+
1
,
1
<
=
i
<
n
,
f
(
s
)
=
∑
d
i
d_i=s_is_{i+1},1<=i<n,f(s)=\sum d_i
di=sisi+1,1<=i<n,f(s)=∑di。现允许交换字符串
s
s
s中的相邻字符,最多交换k次。问最终能得到的、最小的
f
(
s
)
f(s)
f(s)
思路:影响 f ( s ) f(s) f(s)的大小,便是字符1。抓住本质贡献度。
- 对于中间元素的1,它的贡献恒为10+1。
- 对于首元素1,它的贡献为10。
- 对于尾元素的1,它的贡献为1。
比如101101这个字符串。首字符1它贡献了1;而第3个字符1,它分别贡献了01中的1,11中的10,总共贡献了1+10;对于第4个字符1,它贡献了11中的1,10中的10,总共贡献了1+10;对于尾字符1,它贡献了1。
因此,如果结尾元素不是1,我们则优先把1交换到该位置;其次,如果首元素非1,我们尝试把1交换过去。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200010;
const int mx = 1e9+1;
int n, k;
char s[maxn];
void solve() {
scanf("%d%d", &n, &k);
scanf("%s", s);
// plen:交换1到首位的代价
// slen:交换1到尾位的代价
// pre:与首位交换的位置
// suf: 与尾位交换的位置
int pre = 1, suf = n-2, plen = mx, slen = mx;
if (s[0] == '0') {
while (pre < n && s[pre] == '0') ++pre;
if (pre < n - 1) plen = pre;
}
if (s[n-1] == '0') {
while (suf >= 0 && s[suf] == '0') --suf;
if (suf >= 0) slen = n - 1 - suf;
}
if (k >= slen) {
k -= slen;
swap(s[suf], s[n-1]);
}
if (k >= plen) {
k -= plen;
swap(s[pre], s[0]);
}
// printf("new s:%s\n", s);//debug
int res = 0, tmp;
for (int i = 0; i < n - 1; ++i) {
tmp = (s[i] - '0') * 10 + s[i+1] - '0';
res += tmp;
}
printf("%d\n", res);
}
int main() {
int t;
scanf("%d", &t);
int sd = 1;// debug
while (t--) {
// printf("%d:\n", sd++);
solve();
}
}
/*
20 1000000000
10110011110100100100
3 10
001
3 10
100
2 10
01
2 10
10
3 10
010
5 10
00100
4 2
0100
*/
端午快乐,听取wa声一片。。。