[R68C] 这是一道01串题3
- 难度 入门
- 时限 1s
- 空限 512m
- 贪心
数据规模:, 为偶数。
思路
设原串中 0 的个数为 、1 的个数为 。每修改一个字符,只会把一个 0 变成 1 或反过来,使 变化 或 ,因此最少修改次数固定为
且 次修改必须全部同方向: 时只能把 1 改成 0, 时只能把 0 改成 1(否则凑不平数量差)。
在修改次数固定的前提下,再考虑字典序最小:
- :把
1改成0会让该位置变小,位置越靠前收益越大,所以改最靠左的 个1; - :把
0改成1会让该位置变大,为了让改动尽量靠后,所以改最靠右的 个0。
从左到右扫描一遍即可同时完成计数和选位(选最靠右的 个 0 时,维护「当前及之后还剩多少个 0」,剩余数不超过 的 0 即被选中)。
复杂度:时间 ,空间 。
仓颉实现
import std.env.*
import std.convert.*
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let s = reader.readln().getOrThrow()
let runes = s.toRuneArray()
let nn = n
var cnt0 = 0
for (r in runes) {
if (r == r'0') {
cnt0++
}
}
let cnt1 = nn - cnt0
let m = if (cnt0 > cnt1) { (cnt0 - cnt1) / 2 } else { (cnt1 - cnt0) / 2 }
println(m)
if (m == 0) {
println("")
return 0
}
let sb = StringBuilder()
var printed = 0
if (cnt1 > cnt0) {
// 1 多:把最靠左的 m 个 1 改成 0
for (i in 0..nn) {
if (printed == m) {
break
}
if (runes[i] == r'1') {
if (printed > 0) {
sb.append(" ")
}
sb.append(i + 1)
printed++
}
}
} else {
// 0 多:把最靠右的 m 个 0 改成 1
var zerosLeft = cnt0
for (i in 0..nn) {
if (runes[i] == r'0') {
if (zerosLeft <= m) {
if (printed > 0) {
sb.append(" ")
}
sb.append(i + 1)
printed++
}
zerosLeft--
}
}
}
println(sb.toString())
return 0
}
要点:
- 两个方向的分支都从左到右输出下标,天然满足输出「按升序排列」的要求。
- 时第二行输出空行即可。