回家的路
数据规模:,,字符串 长度为 ,只含 0 和 1,首尾字符均为 1。
思路
设 表示从位置 1 跳到位置 所需的最少跳跃次数,无法到达时为 。初始 。
位置 只能从 中的落脚点跳来,因此转移为
也就是说,每处理一个 ,只需要查询滑动窗口 内所有落脚点中 值的最小值。用单调队列维护这个窗口:队首始终是 最小且未滑出窗口的下标,处理 时先把队首超出窗口的元素弹出,再取队首转移;随后把 从队尾插入,插入前弹出队尾所有 值不小于 的元素,保证队列中 值单调递增。
只有 的位置才能落脚,因此 的位置直接跳过(不转移、不入队), 且队列为空说明窗口内没有落脚点, 保持 。最终若 输出 -1,否则输出 。
复杂度
时间 ,额外空间 。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
main() {
let reader = getStdIn()
let first = reader.readln().getOrThrow().split(" ", removeEmpty: true)
let n = Int64.parse(first[0])
let d = Int64.parse(first[1])
let s = reader.readln().getOrThrow()
let INF: Int64 = 1000000000
let nn = n
let dp = Array<Int64>(nn + 1, { _ => INF })
let dq = Array<Int64>(nn + 1, { _ => 0 })
var head: Int64 = 0
var tail: Int64 = 0
dp[1] = 0
dq[tail] = 1
tail += 1
var i: Int64 = 2
while (i <= n) {
if (s[i - 1] == 49) {
while (head < tail && dq[head] < i - d) {
head += 1
}
if (head < tail) {
dp[i] = dp[dq[head]] + 1
}
while (head < tail && dp[dq[tail - 1]] >= dp[i]) {
tail -= 1
}
dq[tail] = i
tail += 1
}
i += 1
}
if (dp[n] >= INF) {
println(-1)
} else {
println(dp[n])
}
}