classSolution: defminCostConnectPoints(self, points: List[List[int]]) -> int: calcu_distance = lambda x, y: abs(points[x][0] - points[y][0]) + abs(points[x][1] - points[y][1]) n = len(points) pq = PriorityQueue() # 利用优先级队列以logN时间开销存储 un_visit = set([i for i in range(n)]) res = 0 cur = (0, 0) # distance, node index pq.put(cur) while un_visit: dist, cur = pq.get() # print('{}'.format(un_visit)) if cur notin un_visit: continue un_visit.remove(cur) res += dist for each in un_visit: dist = calcu_distance(cur, each) pq.put((dist, each)) # print('{}'.format(list(pq.queue))) return res
classSolution: defminCostConnectPoints(self, points: List[List[int]]) -> int: n = len(points) uf = {i: i for i in range(n)} deffind(x): while x != uf[x]: x = uf[x] return x defconnect(p, q): return find(p) == find(q) defunion(p, q): ifnot connect(p, q): uf[find(p)] = find(q) edges = defaultdict(int) for i in range(n): for j in range(i + 1, n): edges[(i, j)] = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]) edges = sorted(edges.items(), key=lambda item:item[1]) # 排序后的边 # print(edges) res = 0 total = 0 for i in range(len(edges)): (x, y), dist = edges[i] # print('x: {}, y: {}'.format(find(x), find(y))) if find(x) != find(y): # 如果还没有连通 total += 1 res += dist # print('{}, {}, {}'.format((x, y), res, total)) if total == n - 1: # 所有节点都已经连通 return res union(x, y) return res
from collections import defaultdict from queue import Queue from typing import List """BFS、并查集"""
classSolution: defisBipartite(self, graph: List[List[int]]) -> bool: n = len(graph) parent = [i for i in range(n)]
deffind(node): while parent[node] != node: node = parent[node] return node
defunion(node1, node2): if find(node1) != find(node2): parent[find(node1)] = find(node2)
for i, each in enumerate(graph): for j in each: union(i, j) # 所有子图中,每个子图找一个随机节点 roots = set() for i in range(n): cur = find(i) if cur notin roots: roots.add(cur) visited = defaultdict(bool) # 要把每个节点都遍历 undo, green, red = 0, 1, -1 color = defaultdict(int) # 0.未着色,1.蓝色,-1.红色 cur_color = 1 q = Queue() # print(roots) for each in roots: q.put(each) visited[each] = True color[each] = red
whilenot q.empty(): cur = q.get() cur_color = color[cur] wanted_color = red if cur_color == green else green for nei in graph[cur]: if color[nei] == undo: color[nei] = wanted_color elif color[nei] != wanted_color: returnFalse color[nei] = wanted_color ifnot visited[nei]: visited[nei] = True q.put(nei) returnTrue
三、图连通
有 n 座城市,编号从 1 到 n 。编号为 x 和 y 的两座城市直接连通的前提是: x 和 y 的公因数中,至少有一个 严格大于 某个阈值 threshold 。更正式地说,如果存在整数 z ,且满足以下所有条件,则编号 x 和 y 的城市之间有一条道路:
x % z == 0 y % z == 0 z > threshold 给你两个整数 n 和 threshold ,以及一个待查询数组,请你判断每个查询 queries[i] = [ai, bi] 指向的城市 ai 和 bi 是否连通(即,它们之间是否存在一条路径)。
返回数组 answer ,其中answer.length == queries.length 。如果第 i 个查询中指向的城市 ai 和 bi 连通,则 answer[i] 为 true ;如果不连通,则 answer[i] 为 false 。
classUF: def__init__(self, n): self.uf = [-1for i in range(n)] self.count_= n
defunion(self, x, y): ux, uy = self.find(x), self.find(y) if ux == uy: return # 规模小的优先合并 if self.uf[ux] < self.uf[uy]: self.uf[ux] += self.uf[uy] self.uf[uy] = ux else: self.uf[uy] += self.uf[ux] self.uf[ux] = uy self.count_ -= 1 deffind(self, x): r = x while self.uf[x] >= 0: x = self.uf[x] # 路径压缩 while r != x: self.uf[r],r = x,self.uf[r] return x
classSolution: defareConnected(self, n: int, threshold: int, queries: List[List[int]]) -> List[bool]: uf = UF(n + 1) # 通过连接大于阈值的因子与所有可能的节点来判断连通,时间复杂度O(n·logn) for i in range(threshold + 1, n + 1): for j in range(2 * i, n + 1, i): uf.union(i, j)
ans = [] for a, b in queries: ans.append(Trueif uf.find(a) == uf.find(b) elseFalse) return ans
classUnionFind: def__init__(self, n): self.uf = [-1] * n self.count_ = n
deffind(self, x): r = x while self.uf[x] >= 0: x = self.uf[x] # x为根节点 # 路径压缩,摊平 while r != x: self.uf[r], r = x, self.uf[r] return x
defunion(self, x, y): ux, uy = self.find(x), self.find(y) if ux == uy: return # 按秩合并,把规模大的合并到规模小的群里,群的元素数量是-uf[root] if self.uf[ux] < self.uf[uy]: self.uf[ux] += self.uf[uy] self.uf[uy] = ux else: self.uf[uy] += self.uf[ux] self.uf[ux] = uy self.count_ -= 1
defcount(self):# 连通子图的数量 return self.count_
classSolution: deflongestConsecutive(self, nums: List[int]) -> int: uf = UnionFind(len(nums)) value2idx = {} # O(n) for i, a in enumerate(nums): if a notin value2idx: value2idx[a] = i visited = set() # O(n) for i, a in enumerate(nums): if a in visited: # 排除已经处理的中间值 continue visited.add(a) if a + 1in value2idx and a + 1notin visited: uf.union(i, value2idx[a + 1]) if a - 1in value2idx and a - 1notin visited: uf.union(i, value2idx[a - 1]) max_ = 0 for i in range(len(nums)): max_ = max(max_, -uf.uf[uf.find(i)]) return max_
classSolution: defdomino(self, n: int, m: int, broken: List[List[int]]) -> int: """匈牙利算法,二分图最大匹配""" edges = defaultdict(set) broken = set([i * m + j for i, j in broken])
defneighbor(x, y): if x > 0: yield (x - 1) * m + y if y > 0: yield x * m + y - 1 if x + 1 < n: yield (x + 1) * m + y if y + 1 < m: yield x * m + y + 1
# 建一个二分图, O(m * n) left_nodes = [] for i in range(n): for j in range(m): point = i * m + j if point in broken: continue if (i + j) % 2 == 0: left_nodes.append(point) for each in neighbor(i, j): if each notin broken: edges[point].add(each) # 匈牙利算法求最大匹配数量,O(|V| * |E|) = O(m * n * m * n) match_l = defaultdict(lambda: None) match_r = defaultdict(lambda: None)
defdfs(p, visited): nonlocal match_l, match_r visited.add(p) for neighbor_p in edges[p]: # 直接找到了一个连接非匹配点的非匹配边 if match_r[neighbor_p] isNone: match_l[p] = neighbor_p match_r[neighbor_p] = p returnTrue
for neighbor_p in edges[p]: nxt = match_r[neighbor_p] if nxt in visited: # 增广路不要涉及重复节点 continue # 递归寻找下一组 左 -> 右,如果右边是非匹配节点,那么递归的逆转增广路选择 if dfs(nxt, visited): match_l[p] = neighbor_p match_r[neighbor_p] = p returnTrue returnFalse
res = 0 for node in left_nodes: if dfs(node, set()): res += 1 return res defdomino_dp(self, n: int, m: int, broken: List[List[int]]) -> int: """插头DP""" valid = defaultdict(bool) NO, HENG, SHU = 0, 1, 2 # 记录坏掉的地板,这里不能放东西 for x, y in broken: valid[x * m + y] = True # 最后增加一行坏掉的地板,表示最后一行只能横着放 for i in (m * n, (m + 1) * n): valid[i] = True # 0代表不放,1代表横着放,2代表竖着放,用m位三进制表示轮廓线的状态 # [3 ^ m, 3] states_transition = [[None] * 3for _ in range(3 ** m)] elements = {} first = lambda s: s // (3 ** (m - 1)) # 轮廓线第一个元素的状态,在当前元素上方 second = lambda s: s % (3 ** (m - 1)) // (3 ** (m - 2)) last = lambda x: x % 3# 轮廓线最后一个元素的状态,在当前元素左边(如果x > 0) row = lambda p: p // m col = lambda p: p % m # 预先计算轮廓线状态转移 for i in range(3 ** m): f, l = first(i), last(i) sec = second(i) elements[i] = (f, sec, l) remain = i % (3 ** (m - 1)) states_transition[i] = [ remain * 3 + NO, remain * 3 + HENG, remain * 3 + SHU ]
@lru_cache(None) defdfs(pos, state): x, y = row(pos), col(pos) # 终止条件 if pos == m * n: return0 first, second, last = elements[state] # 当前位置不能放置 new_state = states_transition[state][NO] ans = dfs(pos + 1, new_state) if first == SHU or (y > 0and last == HENG) or valid[pos]: ... else: # 可以横着放 if y + 1 < m andnot valid[pos + 1] and second != SHU: new_state = states_transition[state][HENG] ans = max(ans, 1 + dfs(pos + 1, new_state)) # 可以竖着放 if x + 1 < n andnot valid[pos + m]: new_state = states_transition[state][SHU] ans = max(ans, 1 + dfs(pos + 1, new_state)) return ans