2101. Detonate the Maximum Bombs
class Solution:
def maximumDetonation(self, bombs: List[List[int]]) -> int:
def detonate(i, j):
a, b, c = bombs[i]
x, y, z = bombs[j]
return (a-x)**2 + (b-y)**2 <= c * c
neighs = collections.defaultdict(list)
i = 0
n = len(bombs)
for i in range(n):
for j in range(n):
if i == j: continue
if detonate(i, j):
neighs[i].append(j)
re = 0
for i in range(n):
count = 0
layer = [i] # bfs
visited = set()
while layer:
j = layer.pop(0)
if j in visited: continue
count += 1
visited.add(j)
for ni in neighs[j]:
layer.append(ni)
re = max(re, count)
if re == n:
return re
return re
1197. Minimum Knight Moves
class Solution:
def minKnightMoves(self, x: int, y: int) -> int:
'''
X,Y四象限的四面是对称的,只要BFS一面, bfs不要走重复的路径
'''
q = [(0, 0)]
level = 0
x, y = abs(x), abs(y)
visited = set((0, 0))
while q:
nq = []
for a, b in q:
if a == x and b == y:
return level
else:
for dx, dy in [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)]:
na, nb = a + dx, b + dy
if na >= -1 and nb >= -1 and (na, nb) not in visited: # 必须包涵-1的位置,不然[3,0]3步走不到
nq.append([na, nb])
visited.add((na, nb))
q = nq
level += 1
1192. Critical Connections in a Network
class Solution:
# https://www.youtube.com/watch?v=mKUsbABiwBI
def criticalConnections(self, n: int, connections: List[List[int]]) -> List[List[int]]:
# 找环,属于环的部分的边全部排除,剩下的
# create graph
graph = collections.defaultdict(list)
for a, b in connections:
graph[a].append(b)
graph[b].append(a)
self.start = 0
mark = {}
update = {} # dfs返回上来的最小值
discard = set()
def dfs(node, pre): # 先把所有的点按照dfs访问的顺序 mark 为从0 到n-1,再返回这个点reach到的最小值
if node not in mark:
mark[node] = self.start
self.start += 1
else:
return mark[node]
minV = mark[node]
for neig in graph[node]:
if neig != pre:
backV = dfs(neig, node)
if backV <= mark[node]: # <= 不能只是<
discard.add(tuple(sorted((node, neig)))) # 记录下要discard的edge 要排序
minV = min(minV, backV)
update[node] = minV
return minV
dfs(0, -1)
re = []
# print(discard)
for a, b in connections:
if tuple(sorted([a,b])) not in discard:
re.append([a, b])
return re
711. Number of Distinct Islands II
class Solution:
def numDistinctIslands2(self, grid: List[List[int]]) -> int:
'''
思路就是每个island计算island里面所有点和点之间的距离,这些距离的counter,会是Unique的
'''
R, C = len(grid), len(grid[0])
hasIsland = 0
def getIsland(r, c, nodeList): # dfs
nodeList.append((r, c))
grid[r][c] = 0
for nr, nc in [(r+1, c), (r-1, c), (r, c+1), (r, c-1)]:
if 0 <= nr < R and 0 <= nc < C and grid[nr][nc]:
getIsland(nr, nc, nodeList)
def computeNodeDistance(nodeList, counter): # 计算所有点之间的距离,包括点和自己,因为有的island只有一个点
for i in range(len(nodeList)):
for j in range(i, len(nodeList)):
dis = (nodeList[i][0] - nodeList[j][0])**2 + (nodeList[i][1] - nodeList[j][1])**2
counter[dis] += 1
distinct = collections.defaultdict(int)
for r in range(R):
for c in range(C):
if grid[r][c] == 1:
hasIsland = 1
nodeList = []
getIsland(r, c, nodeList)
counter = collections.defaultdict(int)
computeNodeDistance(nodeList, counter)
# print(counter)
distinct[tuple([(k, counter.get(k)) for k in sorted(counter)])] += 1 # 这个counter 得排序最后distinct的key才是有序的
# for k in distinct:
# print(k)
# print(distinct[k])
# print("----")
return len([k for k in distinct]) or hasIsland
1376. Time Needed to Inform All Employees
class Solution:
def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int:
# 自己做的不是简洁的写法,简洁的要参考dfs的解法
m2s = collections.defaultdict(list) # manager to sub map
s2m = {} # sub to his manager
for i, m in enumerate(manager):
m2s[m].append(i)
s2m[i] = m
# bfs, count the total time of each path till leaf
count = collections.defaultdict(int) # key is the employeeid, value it the count of time from top till this employee
count[-1] = 0 # for edge case that max(count.values()) count.values() count be empty
queue = [headID]
while queue:
eid = queue.pop(0)
pre = s2m[eid]
if pre != -1:
count[eid] += informTime[pre] + count[pre] # the time his manager need to infom him + the total previosu time
# print(eid, pre)
# print(count)
if m2s[eid]: # if he has subs
# add all his subs to the queue
queue.extend(m2s[eid])
return max(count.values())
'''
dfs
graph = defaultdict(list) # manager to subs
for i in range(len(manager)):
graph[manager[i]].append(i)
def dfs(u):
ans = 0
for v in graph[u]:
ans = max(dfs(v) + informTime[u], ans)
return ans
return dfs(headID)
'''
2115. Find All Possible Recipes from Given Supplies
class Solution:
def findAllRecipes(self, recipes: List[str], ingredients: List[List[str]], supplies: List[str]) -> List[str]:
# have a map to map from recip to ingres which are not in supplies
# dfs recip
ingres = {recipe: [] for recipe in recipes} # recipe to ingres map (elimiate those ingre in supplies)
supplies = set(supplies)
re = []
for i, recip in enumerate(recipes):
for ingre in ingredients[i]:
if ingre not in supplies:
ingres[recip].append(ingre if ingre in ingres else recip) # 如果不在recipes里面也不在supplies里面,就放自己
#这样的话由于21行,dfs的时候就一定能会返回false
print(ingres)
can_make = {}
def dfs(recip):
if recip in can_make:
return can_make[recip]
can_make[recip] = False
if all(dfs(ingre) for ingre in ingres[recip]): # all([]) = True
# print(recip, ingres[recip])
can_make[recip] = True
return can_make[recip]
for recip in recipes:
if dfs(recip):
re.append(recip)
return re
2096. Step-By-Step Directions From a Binary Tree Node to Another
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str:
"""Return lowest common ancestor of start and dest nodes."""
def lca(node):
if not node or node.val in (startValue, destValue):
return node
left, right = lca(node.left), lca(node.right)
return node if left and right else (left or right)
common = lca(root)
stack = [(common, "")] # node, path
path_up = path_down = ""
while stack: # dfs
node, path = stack.pop()
if node.val == startValue:
path_up = path
if node.val == destValue:
path_down = path
if node.left: stack.append((node.left, path + "L"))
if node.right: stack.append((node.right, path + "R"))
return "U" * len(path_up) + path_down
818. Race Car
class Solution:
def racecar(self, T: int, depth = 0) -> int:
# T > 0
q = collections.deque([[ 0, 1 ]])
seen = set('0,1')
while True:
k = len(q)
while k:
[ pos, vel ] = q.popleft()
if pos == T:
return depth # 🎯 target T found
cand = [] # next layer
if pos + vel > 0: # prune, check the dp idea, go back to somewhere>=0
cand.append([ pos + vel, 2 * vel ]) # A
cand.append([ pos, 1 if vel < 0 else -1 ]) # R
for pos, vel in cand:
if f'{pos},{vel}' not in seen:
q.append([ pos, vel ])
seen.add(f'{pos},{vel}')
k -= 1
depth += 1
return -1
'''
https://leetcode.com/problems/race-car/discuss/227415/Figures-to-make-the-DP-solution-more-straightforward
dp:
1. either go forward until pass the target and then go back
2. Or go forward until one step before target, then go back to somewhere >=0, then go forward again to the target.
'''
dp = {0: 0}
def racecar(self, t):
if t in self.dp:
return self.dp[t]
n = t.bit_length()
if 2**n - 1 == t: # 0, 1, 3, 7 = 2**n-1
self.dp[t] = n
else:
self.dp[t] = self.racecar(2**n - 1 - t) + n + 1 # n step + 1 R
for m in range(n - 1):
self.dp[t] = min(self.dp[t], self.racecar(t - 2**(n - 1) + 2**m) + n + m + 1) # link 里面的case1
return self.dp[t]
1293. Shortest Path in a Grid with Obstacles Elimination
from collections import deque
class Solution:
def shortestPath(self, grid: List[List[int]], k: int) -> int:
'''
Solution Explanation
Because we are trying to find the shortest path, use BFS here to exit immediately when a path reaches the bottom right most cell.
Use a set to keep track of already visited paths. We only need to keep track of the row, column, and the eliminate obstacle usage count. We don't need to keep track of the steps because remember we are using BFS for the shortest path. That means there is no value storing a 4th piece of the data, the current steps. This will reduce the amount of repeat work.
m = rows
n = columns
k = allowed elimination usages
Time Complexity
O(m*n*k) time complexity
This is because for every cell (m*n), in the worst case we have to put that cell into the queue/bfs k times.
Runtime: 68 ms, faster than 33.33% of Python3 online submissions
Space Complexity
O(m*n*k) space complexity
This is because for every cell (m*n), in the worst case we have to put that cell into the queue/bfs k times which means we need to worst case store all of those steps/paths in the visited set.
'''
if len(grid) == 1 and len(grid[0]) == 1:
return 0
queue = deque([(0,0,k,0)]) # 看下面
visited = set([(0,0,k)])
if k > (len(grid)-1 + len(grid[0])-1):
return len(grid)-1 + len(grid[0])-1
while queue:
row, col, eliminate, steps = queue.popleft()
for new_row, new_col in [(row-1,col), (row,col+1), (row+1, col), (row, col-1)]:
if (new_row >= 0 and
new_row < len(grid) and
new_col >= 0 and
new_col < len(grid[0])):
if grid[new_row][new_col] == 1 and eliminate > 0 and (new_row, new_col, eliminate-1) not in visited:
visited.add((new_row, new_col, eliminate-1))
queue.append((new_row, new_col, eliminate-1, steps+1))
if grid[new_row][new_col] == 0 and (new_row, new_col, eliminate) not in visited:
if new_row == len(grid)-1 and new_col == len(grid[0])-1:
return steps+1
visited.add((new_row, new_col, eliminate))
queue.append((new_row, new_col, eliminate, steps+1))
return -1