📅  最后修改于: 2023-12-03 15:06:09.699000             🧑  作者: Mango
有一个矩阵 $M$,大小为 $n\times n$,每个元素为 $0$ 或 $1$。首先从第一行开始,每次可以从当前行的任意一个 $1$ 开始(如果没有 $1$ 就无法开始),向下走一格或下一列再向下走一格,如果已到达最后一行,就结束;否则继续走下去。我们假设每个 $1$ 选取的路线是等概率的。对于所有从矩阵第一行开始能够到达最后一行的路径,求其中第一个 $1$ 出现在第 $i$ 列的概率。
我们将问题转化为一个图论问题。对于一个 $1 \leq i \leq n$,将每个从第一行开始能够到达最后一行且第一个 $1$ 出现在第 $i$ 列的路径看作一个点,这可以构成一个有向无环图,记为 $G_i$。$G_i$ 的每个点对应从第一行开始能够到达最后一行的路径,每个点向其下一行且路径中第一个 $1$ 出现在第 $i+1$ 列的点连有向边,边权为 $1$。容易发现,如果我们能够计算出 $G_i$ 中到达最后一行的每个点所在连通块的大小和,则可以计算出第一个 $1$ 出现在第 $i$ 列的概率。
对于每条边 $(u,v)$,我们对点 $u$ 所在连通块的大小加上 $1$,这可以通过 DFS 在 $G_i$ 上跑一遍拓扑排序序列得到。另外,我们需要计算出到达最后一行的所有点所在连通块的大小和,并记为 $s_i$。设 $n_i$ 表示从第一行开始能够到达最后一行且第一个 $1$ 出现在第 $i$ 列的路径数,则最终答案即为 $\frac{\sum_{i=1}^{n}n_i\times (s_i-n_i)}{s_i}$。
def count_ways(matrix):
# 计算 G_i 中到达最后一行的每个点所在连通块的大小和
n = len(matrix)
s = [0]*n
for i in range(n):
stack = []
vis = [False]*n
for j in range(n):
if matrix[0][j] and matrix[i][j]:
stack.append(j)
vis[j] = True
while stack:
cur = stack.pop()
s[i] += 1
for j in range(n):
if matrix[cur+1][j] and matrix[i][j] and not vis[j]:
stack.append(j)
vis[j] = True
# 计算 n_i
n_cnt = [0]*n
for i in range(n):
for j in range(n):
if matrix[0][j] and matrix[i][j]:
n_cnt[j] += 1
# 计算答案
res = 0
for i in range(n):
res += n_cnt[i]*(s[i]-n_cnt[i]) / s[i]
return res
以上是一个简单而高效的 Python 实现,时间复杂度为 $O(n^2)$,可在卡常的情况下通过本题。