LC 2612. 最少翻转操作数
https://leetcode.cn/problems/minimum-reverse-operations/
这题最开始想到了使用BFS来搞,但是中间有两个问题:
- 枚举从i可以置换到的位置,说实话这个有点绕
- 如果k很大的话,那么每次i可以枚举的位置会很多,怎么进行剪枝。
关于2,通常我们BFS的时候边不会太多,但是这个题目边会特别多。所以我们不能枚举点再去check, 而必须去筛选那些没有被选中的点。
关于1,我一开始有过许多奇奇怪怪的枚举办法,但是都有各种各样的问题:
- 首先可以置换位置必须满足奇偶性,(x + y + 1)% 2 == k % 2
- 我们可以先求最低位,然后不断向上遍历,直到条件不满足位置。
- 不满足的条件就是, 如果(x, y)交换,那么可以延展的范围必须在数组内。
- 我们在筛选那些没有选中点的时候,必须也考虑到奇偶性,不然搜索空间就会很大了。
class Solution: def minReverseOperations(self, n: int, p: int, banned: List[int], k: int) -> List[int]: from sortedcontainers import SortedList opts = [SortedList(), SortedList()] for i in range(n): opts[i % 2].add(i) for x in banned: opts[x % 2].remove(x) from collections import deque dq = deque() ans = [-1] * n dq.append(p) ans[p] = 0 opts[p % 2].remove(p) def search(x): y = max(x + 1 - k, k - x - 1, 0) if (x + y + 1) % 2 != k % 2: y += 1 opt = opts[y % 2] idx = opt.bisect_left(y) while idx < len(opt): y = opt[idx] a, b = x, y if a > b: a, b = b, a m = (k - (b - a + 1)) // 2 if m >= 0 and (a - m) >= 0 and (b + m) < n: yield a if a != x else b else: break idx += 1 while dq: x = dq.popleft() values = [] for y in search(x): values.append(y) print(x, values) for v in values: opts[v % 2].remove(v) for y in values: ans[y] = ans[x] + 1 dq.append(y) return ans