链接: 1514. 概率最大的路径
难度:中等
给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b] 表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 **1e-5 **,就会被视作正确答案。
示例 1:

输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
解释:从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 * 0.5 = 0.25
示例 2:

输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
输出:0.30000
示例 3:

输入:n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
输出:0.00000
解释:节点 0 和 节点 2 之间不存在路径
提示:
2 <= n <= 10^40 <= start, end < nstart != end0 <= a, b < na != b0 <= succProb.length == edges.length <= 2*10^40 <= succProb[i] <= 1Djikstra
class Solution:
def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
graph = defaultdict(dict)
for i in range(len(edges)):
a,b = edges[i]
p = succProb[i]
graph[a][b] = 1-p
graph[b][a] = 1-p
def calc(p1,p2):
return 1-(1-p1)*(1-p2)
def dijkstra(graph,start):
from queue import PriorityQueue
dist = collections.defaultdict(lambda:1.0) # 初始化距离数组
dist[start] = 0 # 原点到自己是0
visited = set([start]) # 访问过原点了
q = PriorityQueue()
for v,w in graph[start].items(): # 找到所有原点的邻居,更新他们的dist
dist[v] = w
q.put((w,v)) # 权放前边,注意是put
while not q.empty():
x,u = q.get() # 用u给别的节点做松弛,注意是get
if u in visited:
continue
visited.add(u)
for v,w in graph[u].items():
new_dist = calc(dist[u],w)
if new_dist < dist[v]:
dist[v] = new_dist
if v not in visited:
q.put((new_dist,v))
return dist
dist = dijkstra(graph,start)
return 1-dist[end]
链接: LCP 56. 信物传送

这题我们要求的是最少操作次数,而每个块都可以操作,因此实际的状态转移依然是四个防线,遍历是否操作即可。
class Solution:
def conveyorBelt(self, matrix: List[str], start: List[int], end: List[int]) -> int:
m,n = len(matrix),len(matrix[0])
end = (end[0],end[1])
visited = {(start[0],start[1]):0}
q = [(0,start[0],start[1])]
def in_bound(x,y):
return 0<=x<m and 0<=y<n
dirs = [
(0,1),
(0,-1),
(1,0),
(-1,0)
]
while q:
change,i,j = heapq.heappop(q)
if (i,j) == end:
return change
# print(change,i,j)
cur = matrix[i][j]
for a,b in dirs:
x,y = i+a,j+b
new_change = change+1
if cur == '>' and (a,b) == (0,1):
new_change -= 1
elif cur == '<' and (a,b) == (0,-1):
new_change -= 1
elif cur == '^' and (a,b) == (-1,0):
new_change -= 1
elif cur == 'v' and (a,b) == (1,0):
new_change -= 1
# if (x,y) == end:
# return new_change
if in_bound(x,y) and ((x,y) not in visited or visited[(x,y)]>new_change):
visited[(x,y)] = new_change
heapq.heappush(q,(new_change,x,y))
return -1
链接: 1377. T 秒后青蛙的位置
难度:困难
给你一棵由 n 个顶点组成的无向树,顶点编号从 1 到 n。青蛙从 顶点 1 开始起跳。规则如下:
无向树的边用数组 edges 描述,其中 edges[i] = [fromi, toi] 意味着存在一条直接连通 fromi 和 toi 两个顶点的边。
返回青蛙在 t 秒后位于目标顶点 target 上的概率。
示例 1:
输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 2, target = 4
输出:0.16666666666666666
解释:上图显示了青蛙的跳跃路径。青蛙从顶点 1 起跳,第 1 秒 有 1/3 的概率跳到顶点 2 ,然后第 2 秒 有 1/2 的概率跳到顶点 4,因此青蛙在 2 秒后位于顶点 4 的概率是 1/3 * 1/2 = 1/6 = 0.16666666666666666 。
示例 2:
输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 1, target = 7
输出:0.3333333333333333
解释:上图显示了青蛙的跳跃路径。青蛙从顶点 1 起跳,有 1/3 = 0.3333333333333333 的概率能够 1 秒 后跳到顶点 7 。
提示:
1 <= n <= 100edges.length == n - 1edges[i].length == 21 <= ai, bi <= n1 <= t <= 501 <= target <= n这题看似吓人,地铁上想了一会其实就是要分类讨论。
class Solution:
def frogPosition(self, n: int, edges: List[List[int]], t: int, target: int) -> float:
graph = defaultdict(list)
for u,v in edges:
graph[u].append(v)
graph[v].append(u)
if target == 1:
if t == 0:
return 1.0
else:
if len(graph[1])==0:
return 1
else:
return 0
visited = {1:1.0}
q = deque(visited)
step = 0
while q:
new_q = deque()
step += 1
while q:
cur = q.popleft()
sel = len(graph[cur]) - 1 if cur != 1 else len(graph[cur]) # 下一步能取的位置数,减去父节点。
p = visited[cur] # x 本身带的概率
if sel == 0:
continue
new_p = p*(1/sel)
for nxt in graph[cur]:
if nxt in visited:
continue
if nxt == target :
if t == step:
return new_p
else:
if len(graph[nxt]) == 1:
return new_p
else:
return 0.0
visited[nxt] = new_p
new_q.append(nxt)
if step == t:
return 0.0
q = new_q
return 0