<think>我们分析洛谷 P3232 [HNOI2013]游走这道题。题目要求在一个无向连通图上进行随机游走,从节点1出发,到达节点n结束。每次随机选择一条与当前节点相连的边走到下一个节点,并获得该边的分数(分数为边的编号)。现在需要对边重新编号(1~m),使得总分的期望值最小。注意,我们需要最小化的是总分的期望值。
### 问题分析
1. **目标**:重新给边编号(1~m,分数即为编号),使得从节点1随机游走到节点n所经过的边的编号之和的期望值最小。
2. **关键观察**:期望值最小的策略显然是将经过概率大的边赋予小的编号(因为期望是边权乘以经过概率的和,所以边权小则期望小,但这里边权就是编号,所以我们要让经过概率大的边编号小)。
3. **问题转化**:因此,我们需要计算每条边的期望经过次数。然后,将期望经过次数大的边赋予较小的编号(即1,2,...,m中较小的数),而期望经过次数小的边赋予较大的编号。这样,总期望和就会最小。
### 解题思路
1. **计算每条边的期望经过次数**:设一条边$e=(u,v)$的期望经过次数为$f(e)$。那么,总期望和为$\sum_{e} f(e) \times \text{编号}(e)$。根据排序不等式,当$f(e)$大的边赋予小的编号时,总和最小。因此,问题转化为求每条边的期望经过次数。
2. **边的期望经过次数与点的关系**:对于无向图中的边$(u,v)$,其期望经过次数可以表示为:
$$f(u,v) = \frac{g(u)}{d(u)} + \frac{g(v)}{d(v)} \quad \text{当} \quad u \neq n \quad \text{且} \quad v \neq n$$
其中,$g(u)$表示经过节点$u$的期望次数,$d(u)$是节点$u$的度数。但是注意,当游走到节点n时停止,所以节点n不会再往外走,因此边$(u,n)$的期望经过次数只包含从$u$到$n$的那一次(且$n$不会往外走,所以不会从$n$走到$u$)。实际上,更精确的公式是:
- 从$u$到$v$的期望次数等于在节点$u$时选择边$(u,v)$的概率乘以到达$u$的期望次数,再加上在节点$v$时选择边$(v,u)$的概率乘以到达$v$的期望次数(但注意,如果$v$是n,则不会从$v$走到$u$)。
- 因此,一般地,有:
$$f(u,v) = \frac{g(u)}{d(u)} \cdot [v \neq n] + \frac{g(v)}{d(v)} \cdot [u \neq n]$$
其中$[P]$是指示函数,当条件$P$成立时为1,否则为0。
3. **计算节点的期望经过次数**:我们需要计算每个节点$u$的期望经过次数$g(u)$。注意起点是1,终点是n(到达n后停止)。
- 对于节点1:其期望经过次数$g(1)$包括初始时刻的一次,以及可能从其他节点走回来。因此,我们需要建立方程组。
- 对于节点$u$($u\neq 1$且$u\neq n$):
$$g(u) = \sum_{(v,u) \in E} \frac{g(v)}{d(v)}$$
注意:这里求和是遍历所有指向$u$的边(无向图,所以就是所有与$u$相邻的节点$v$),并且$v$不能是$n$(因为到达$n$就停止了,不会从$n$再走到其他节点)。
- 对于节点1,因为一开始就在1,所以:
$$g(1) = 1 + \sum_{(v,1) \in E} \frac{g(v)}{d(v)} \quad \text{(注意:v不能是n)}$$
但是,这里有一个循环依赖:每个节点的值依赖于相邻节点的值。因此,我们需要建立线性方程组。
4. **方程组建立**:设节点$u$的期望经过次数为$g(u)$,则:
- $g(1) = 1 + \sum_{v \in N(1)} \frac{g(v)}{d(v)} \quad \text{(注意:这里v不能是n,但实际上如果1和n相邻,那么从v=n过来是不允许的,因为n不会往外走)}$
但是,这个式子并不直接,因为1的相邻节点可能会依赖1。实际上,我们重新考虑:
- 当在节点$u$时,下一步会以$1/d(u)$的概率走到它的每个邻居(除了节点n,因为走到n就停止了,所以节点n不会继续往外走,但是节点u可以是n吗?注意u=n时已经停止,所以不考虑从n出发)。因此,节点u的期望经过次数等于所有邻居节点v(v≠n)的期望经过次数乘以从v走到u的概率(即$1/d(v)$)的和,再加上初始状态(如果是起点1,则多一个1)。
- 因此,我们可以写出:
对于节点1:$g(1) = 1 + \sum_{(v,1) \in E, v \neq n} \frac{g(v)}{d(v)}$
对于节点$u$($u\neq 1$且$u\neq n$):$g(u) = \sum_{(v,u) \in E, v \neq n} \frac{g(v)}{d(v)}$
对于节点n:$g(n)=0$(因为到达n就停止了,不会经过n继续走,而且题目要求到达n结束,所以计算经过次数时,我们只计算到达n的次数?但是注意,我们经过n的次数是1次(最后一次),但这里我们定义$g(n)$为到达n的次数,但是实际上在游走过程中,到达n就停止了,所以n的期望经过次数就是1?这里需要澄清。)
然而,实际上,我们定义$g(u)$为从起点1开始到游走结束(到达n)的过程中,经过节点$u$的次数(包括多次经过)。但是,节点n只能经过一次(因为到达n就停止了),所以$g(n)=1$?不对,因为节点n可能是从不同的路径到达的,所以$g(n)$应该是到达n的概率和?不对,期望次数。
实际上,到达n后停止,所以节点n只能被经过一次(因为一旦到达就停止,所以不会再次到达)。但是,期望次数是1吗?不一定,因为可能有多条路径到达n,所以期望次数应该是1(因为最终一定会到达n,所以期望次数为1)。但是,在方程组中,我们如何体现?
重新考虑:节点n是终止节点,所以当我们到达n时,游走结束。因此,节点n的期望经过次数就是1(因为最终一定会到达一次)。但是,在计算其他节点时,从其他节点走到n,那么对于边来说,从其他节点到n的边只会计入一次(因为到达n就停止了)。
因此,我们修正:
- $g(n)=1$(因为最终一定会到达n,所以期望经过次数为1)
- 节点1:$g(1) = 1 + \sum_{(v,1)\in E} \frac{g(v)}{d(v)} \cdot [v\neq n]$
但是,节点1的初始状态算一次,然后从邻居节点(非n)走回来也会增加次数。
- 对于其他节点$u$($u\neq 1$且$u\neq n$):
$$g(u) = \sum_{(v,u)\in E} \frac{g(v)}{d(v)} \cdot [v\neq n]$$
但是,这个方程组存在循环依赖,而且节点1的方程中包含了自身(因为无向图中1的邻居可能包含1吗?无向简单图,没有自环,所以不会包含1)。然而,节点1的方程中出现了$g(v)$,而$v$可能是1的邻居,这些邻居又可能包含1(形成环),所以我们需要解线性方程组。
然而,这里有一个问题:节点n的期望次数是1,那么节点n的方程应该怎么写?实际上,节点n不会继续往外走,所以它不会对邻居的期望次数产生贡献(即不会通过$g(n)/d(n)$去贡献其他节点)。但是,节点n的期望次数$g(n)$是1,这个1是确定的。
所以,我们重新定义方程组:
令$g(1)$表示经过节点1的期望次数(包括初始的那一次),$g(n)=1$(表示最终一定会经过n一次)。
对于节点$u$($u\neq 1$且$u\neq n$),有:
$$g(u) = \sum_{v \in N(u)} \frac{g(v)}{d(v)} \quad \text{(注意:这里要求$v\neq n$,因为从$v$到$u$的转移必须要求$v$不是终止节点)}$$
但是,节点1的方程:$g(1) = 1 + \sum_{v\in N(1), v\neq n} \frac{g(v)}{d(v)}$(因为初始在1,然后从邻居(非n)回来会增加次数)
节点n的方程:$g(n)=1$(不参与其他节点的转移,因为到达n就停止了)
然而,这个方程组并不完整,因为节点1的邻居可能包含n吗?可以包含。如果节点1和n相邻,那么从节点1可以直接走到n。但是,在节点1的方程中,我们并没有考虑从1走到n的情况?实际上,节点1的期望次数只计算停留在1的次数,而走到n后游走结束,不会增加其他节点的次数。所以,在节点1的方程中,我们只考虑从其他节点走到1的情况(不包括从n走到1,因为n不会往外走)。所以,如果1和n相邻,那么节点1走到n并不会影响节点1的期望次数(因为节点1的期望次数是停留在1的次数,而走到n是离开1)。
实际上,更标准的做法是:将节点n视为吸收态(一旦到达就停留,不再转移)。那么,我们可以建立如下方程组:
对于每个节点$u$($u\neq n$):
$$g(u) = [u=1] + \sum_{v \in N(u)} \frac{g(v)}{d(v)} \quad (v \neq n)$$
注意:这里$[u=1]$表示如果$u$是1,则初始有1次(即一开始就在1),否则为0。但是,这个方程中,每个节点$u$的期望次数等于所有邻居$v$(非n)的期望次数除以$d(v)$(即从$v$走到$u$的概率)的和,再加上初始在1的那一次(仅当$u=1$时)。然而,这个方程并没有考虑节点$u$的邻居是n的情况?实际上,当$u$的邻居是n时,从$u$到n的转移会终止,所以不会增加其他节点的次数。但是,在计算$g(u)$时,我们只考虑其他节点走到$u$的情况,而$u$自己走到邻居并不会影响$g(u)$(因为$g(u)$是经过$u$的次数,每次经过$u$后,$u$会以均等的概率走到邻居,包括n,但是走到n后不会增加其他节点的次数,所以我们在方程组中不考虑从$u$到n的转移,只考虑其他节点走到$u$)。
然而,这个方程组的建立需要再仔细考虑。实际上,更常见的做法是:
设$g(u)$表示从起点1开始,到达终点n的游走过程中,经过节点$u$的期望次数(注意:到达n后停止,所以节点n只计算一次,且不会从n再转移出去)。
那么,对于节点1,有:
$$g(1) = 1 + \sum_{v \in N(1), v \neq n} \frac{g(v)}{d(v)}$$
为什么?因为一开始在1(所以+1),然后,当从邻居$v$(非n)走到1时,会增加1的经过次数(每次从$v$走到1,都会使1的经过次数增加1次,而$v$的期望次数是$g(v)$,每次$v$有$1/d(v)$的概率走到1,所以是$g(v)/d(v)$)。
对于节点$u$($u\neq 1$且$u\neq n$):
$$g(u) = \sum_{v \in N(u), v \neq n} \frac{g(v)}{d(v)}$$
因为节点$u$的经过次数全部来源于邻居节点(非n)的转移。
对于节点n:
$$g(n)=1$$
因为最终一定会到达n一次(且仅一次,因为到达就停止了,所以不会多次到达)。
但是,这个方程组有一个问题:节点1的方程中,$g(1)$依赖于$g(v)$(v是1的邻居),而v的方程又可能依赖于$g(1)$(因为无向图,有环)。所以我们需要解一个线性方程组。
另外,节点n的方程是已知的($g(n)=1$),所以我们可以将其代入其他方程。但是,在节点u的方程中,我们只考虑非n的邻居,所以实际上节点n不会出现在方程的右边(除了节点n自己,但节点n的方程已经独立)。
然而,节点n的期望次数是1,但其他节点在计算时,如果邻居有n,那么从该节点走到n会终止,所以不会增加其他节点的次数?但是,在计算节点u(u≠n)的期望次数时,我们只考虑其他节点走到u,而不考虑u走到其他节点。所以,节点u的期望次数等于所有非n邻居节点v的期望次数乘以$1/d(v)$(即从v走到u的概率)的和。
因此,方程组为:
$$g(1) - \sum_{v \in N(1), v\neq n} \frac{g(v)}{d(v)} = 1$$
$$g(u) - \sum_{v \in N(u), v\neq n} \frac{g(v)}{d(v)} = 0 \quad (u \neq 1, u\neq n)$$
$$g(n)=1$$
但是,节点n的邻居呢?在计算邻居节点(比如节点u,且u与n相邻)的期望次数时,我们并没有考虑从n走到u的情况(因为n不会往外走),所以节点n的期望次数1不会对任何其他节点产生贡献。因此,节点n的1只表示它自己被经过1次。
然而,这里有一个问题:节点1的方程中,如果1和n相邻,那么从1可以一步走到n,但是节点1的期望次数中,并不包括从1走到n这一步对节点1的贡献?实际上,节点1的期望次数包括:初始在1(1次),以及从其他节点(非n)走到1的次数。而如果1直接走到n,那么节点1的期望次数就是1(因为初始在1,然后直接走到n,没有其他节点走到1)。所以,这个方程是合理的。
但是,节点n的期望次数1是怎么来的?实际上,最终一定会到达n,所以$g(n)=1$。但是,在游走过程中,节点1可能直接走到n,也可能经过其他路径到达n。所以,节点n的期望次数1是固定的。
因此,我们得到$n-1$个未知数($g(1),g(2),\dots,g(n-1)$)的线性方程组($g(n)$已知为1,不需要解)。注意,节点n的邻居节点在计算时,如果$u$是n的邻居,那么$u$的方程中,其邻居$v$不包括n(因为$v\neq n$),所以不会出现$g(n)$。
5. **边的期望经过次数**:得到每个节点的期望经过次数$g(u)$后,对于一条边$e=(u,v)$(假设$u$和$v$都不是n),那么这条边的期望经过次数为:
$$f(e) = \frac{g(u)}{d(u)} + \frac{g(v)}{d(v)}$$
因为从$u$经过这条边到$v$(概率为$1/d(u)$,且$u$的期望经过次数为$g(u)$),以及从$v$经过这条边到$u$(概率为$1/d(v)$,且$v$的期望经过次数为$g(v)$)。但是,如果其中一个是n呢?
- 如果$u=n$,那么边$(n,v)$的期望经过次数:因为从$v$走到$n$($n$不会往回走),所以只有从$v$到$n$的贡献:$f(e) = \frac{g(v)}{d(v)}$。
- 同理,如果$v=n$,则$f(e) = \frac{g(u)}{d(u)}$。
因此,统一写:
$$f(e) = \frac{g(u)}{d(u)} \cdot [u \neq n] + \frac{g(v)}{d(v)} \cdot [v \neq n]$$
6. **最小化总期望**:设我们给边重新编号为$w_1, w_2, \dots, w_m$($w_i$是互不相同的1~m的整数),则总期望为$\sum_{e} f(e) \cdot w(e)$。根据排序不等式,为了最小化这个和,我们应该将期望经过次数$f(e)$大的边赋予小的$w(e)$(即小的编号)。因此,将边按$f(e)$从大到小排序,然后依次赋予编号$1,2,\dots,m$(即最大的$f(e)$赋予1,次大的赋予2,...,最小的赋予m)。
总期望的最小值为:$\sum_{i=1}^{m} f(e_i) \times i$,其中$f(e_i)$是排序后第$i$大的边的期望经过次数(即赋予的编号为$i$)。
7. **解线性方程组**:方程组有$n-1$个方程(节点1和节点2~n-1),未知数为$g(1),g(2),\dots,g(n-1)$。注意节点n的$g(n)=1$已知。方程组的形式为:
$$g(u) - \sum_{v \in N(u), v\neq n} \frac{g(v)}{d(v)} = \begin{cases} 1, & \text{if } u=1 \\ 0, & \text{otherwise} \end{cases}$$
注意,这里$u$的范围是$1$到$n-1$。这个方程组可以用高斯消元求解。
注意:每个节点$u$的方程中,系数矩阵的构建:
- 第$u$行第$u$列:系数为1。
- 第$u$行第$v$列($v$是$u$的邻居,且$v\neq n$):系数为$-1/d(v)$。
- 常数项:$u=1$时为1,否则为0。
但是,这里有一个细节:节点$u$的方程中,$v$是$u$的邻居,而$v$可能是1~n-1中的任意节点(除了n)。另外,节点$u$的邻居$v$的度数$d(v)$是已知的。
由于$n\le 500$,高斯消元$O(n^3)$可以接受。
### 推导公式总结
1. **定义**:
- $g(u)$:节点$u$的期望经过次数($u$从1到n-1),$g(n)=1$。
- $d(u)$:节点$u$的度数(包括与n相邻的边?是的,因为度数就是相邻边的数量,但是节点n不会往外走,所以计算度数时,节点n的度数我们不考虑?实际上,度数在图中是固定的,每个节点的度数在输入时就确定了,包括节点n的度数。但是,在方程中,节点n的邻居不会出现在方程右边,而节点n的度数在计算边经过次数时会用到(如果边与n相连))。
2. **线性方程组**($u=1,2,\dots,n-1$):
$$g(u) - \sum_{v \in N(u), v\neq n} \frac{g(v)}{d(v)} = \begin{cases} 1, & u=1 \\ 0, & u \neq 1, u \neq n \end{cases}$$
注意:节点$u$的邻居$v$必须满足$v\neq n$(即不考虑从n转移过来的情况)。
3. **边的期望经过次数**(对于边$e=(u,v)$):
$$f(e) = \frac{g(u)}{d(u)} \cdot [u \neq n] + \frac{g(v)}{d(v)} \cdot [v \neq n]$$
4. **最小化总期望**:将边按$f(e)$降序排序,赋予编号$1,2,\dots,m$,总期望为:
$$\text{ans} = \sum_{i=1}^{m} i \times f(e_{\text{rank } i})$$
### 算法步骤
1. 读入无向图,记录每个节点的度数$d$,以及邻接表(包括边)。
2. 建立线性方程组(未知数$g(1),g(2),\dots,g(n-1)$):
- 初始化一个$n-1$阶的系数矩阵$A$和常数向量$B$。
- 对于每个节点$u$($1\le u \le n-1$):
$A[u][u] = 1$ (这里假设节点编号从1开始,但矩阵下标从0开始则要调整)
对于$u$的每个邻居$v$($v$在1~n-1之间,即$v\neq n$):
$A[u][v] = -1/d(v)$ (注意:这里$v$是邻居节点,且$v\neq n$)
$B[u] = 1$ 当且仅当$u=1$,否则为0。
- 注意:节点$u$的邻居$v$可能是节点n,此时跳过(因为$v=n$,不在未知数范围内)。
3. 用高斯消元法求解方程组$A \times \vec{g} = \vec{B}$,得到$g(1),g(2),\dots,g(n-1)$。
4. 令$g(n)=1$。
5. 计算每条边$e=(u,v)$的期望经过次数$f(e)$:
$$f(e) = \frac{g(u)}{d(u)} \cdot [u \neq n] + \frac{g(v)}{d(v)} \cdot [v \neq n]$$
6. 将边按$f(e)$从大到小排序。
7. 将排序后的边依次赋予编号$1,2,\dots,m$(即$f(e)$最大的边编号为1,次大的编号为2,...,最小的编号为m)。
8. 计算总期望:$\text{ans} = \sum_{i=1}^{m} i \times f(e_i)$,其中$e_i$是排序后第$i$条边。
### 注意事项
- 节点$n$的期望次数$g(n)$固定为1,不参与方程组求解。
- 在构建方程组时,节点$u$的邻居$v$如果等于$n$,则忽略(因为$n$不在未知数中)。
- 高斯消元求解$n-1$元线性方程组,时间复杂度$O(n^3)$,$n\le 500$,所以$500^3=1.25e8$,在C++中可能需要注意优化(如使用部分主元高斯消元,避免精度问题)。
- 精度问题:使用`double`,注意高斯消元中的精度控制。
### 代码框架(C++)
```cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
const double eps = 1e-8;
int n, m;
vector<int> G[N]; // 邻接表,存邻居节点(包括n)
int deg[N]; // 度数
double g[N]; // g[1..n-1]为未知数,g[n]=1
double A[N][N]; // 增广矩阵,大小为(n-1)*(n)(最后一列是常数)
int u[N*N], v[N*N]; // 存储每条边
void gauss(int n) {
// 高斯消元求解n阶线性方程组(实际未知数为n-1个,这里n表示节点数-1,即n-1个未知数)
int equ = n, var = n; // n个方程,n个未知数(这里n就是n-1,因为节点1~n-1)
for (int i = 1; i <= n; i++) {
// 找到第i列中绝对值最大的行
int r = i;
for (int j = i+1; j <= n; j++)
if (fabs(A[j][i]) > fabs(A[r][i])) r = j;
if (fabs(A[r][i]) < eps) continue; // 无解或多解
if (r != i) swap(A[r], A[i]);
// 归一化
for (int j = i+1; j <= var+1; j++) A[i][j] /= A[i][i];
A[i][i] = 1.0;
// 消元
for (int j = 1; j <= equ; j++) {
if (j == i) continue;
double rate = A[j][i];
for (int k = i; k <= var+1; k++) {
A[j][k] -= rate * A[i][k];
}
}
}
// 回代(实际上增广矩阵最后一行已经是解)
for (int i = 1; i <= n; i++) {
g[i] = A[i][var+1];
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u[i], &v[i]);
G[u[i]].push_back(v[i]);
G[v[i]].push_back(u[i]);
deg[u[i]]++;
deg[v[i]]++;
}
// 建立方程组:未知数g[1]~g[n-1]
// 初始化增广矩阵
memset(A, 0, sizeof(A));
// 第1个方程对应节点1,第n-1个方程对应节点n-1
for (int i = 1; i <= n-1; i++) {
// 第i个方程对应节点i(注意:节点编号i,在矩阵中是第i行)
A[i][i] = 1.0;
for (int j = 0; j < G[i].size(); j++) {
int neighbor = G[i][j];
if (neighbor == n) continue; // 跳过邻居为n的情况
// 注意:邻居neighbor在1~n-1之间
A[i][neighbor] -= 1.0 / deg[neighbor];
}
// 常数项
if (i == 1) A[i][n] = 1.0; // 常数项在增广矩阵的第n列(因为未知数有n-1个,所以常数项放在第n列)
else A[i][n] = 0.0;
}
// 高斯消元求解,未知数个数为n-1
gauss(n-1);
// 设置g[n]=1
g[n] = 1.0;
// 计算每条边的期望经过次数
vector<double> f;
for (int i = 1; i <= m; i++) {
double cnt = 0;
if (u[i] != n) cnt += g[u[i]] / deg[u[i]];
if (v[i] != n) cnt += g[v[i]] / deg[v[i]];
f.push_back(cnt);
}
// 将f从大到小排序
sort(f.begin(), f.end(), greater<double>());
double ans = 0;
for (int i = 0; i < m; i++) {
ans += f[i] * (i+1); // 第i大的边赋予编号i+1(因为编号从1开始)
}
printf("%.3f\n", ans);
return 0;
}
```
注意:上面的高斯消元部分是一个简化的版本,实际实现时要注意精度和主元选取。另外,上面的代码中增广矩阵的常数项放在第n列(即下标为n的列),而未知数的列下标从1到n-1。
### 参考文献
[^1]: Luogu P3232 [HNOI2013]游走题目描述
[^2]: 洛谷题解中关于概率dp和高斯消元的应用
[^3]: 关于终止节点n的处理方式
[^4]: 输入输出格式说明
§§相关问题§§
1. 如何在高斯消元中避免精度问题?
2. 排序不等式在期望问题中的应用还有哪些?
3. 无向图随机游走问题中,节点的期望访问次数有哪些性质?
4. 如果图不是无向图,而是有向无环图,如何计算边的期望经过次数?
5. 高斯消元法的时间复杂度如何优化?</think>### 洛谷 P3232 [HNOI2013]游走 题目分析
#### 问题描述
给定一个无向连通图($n$ 个顶点,$m$ 条边),从节点 1 开始随机游走:每次以均等概率选择当前节点的边移动到相邻节点,并获得该边的编号作为分数。到达节点 $n$ 时游走结束。要求对边重新编号(1 到 $m$),使得总分的期望值最小。
**关键观察**:
期望值最小化策略是让经过概率高的边赋予小编号(根据排序不等式)。因此核心是计算**每条边的期望经过次数**,再按期望次数降序分配编号 1, 2, ..., m。
---
### 解题思路
#### 步骤 1:建立节点期望方程
设 $g(u)$ 表示节点 $u$ 的期望经过次数($u \in [1, n]$),需满足:
1. **起点特殊处理**:$g(1)$ 包含初始状态
$$g(1) = 1 + \sum_{\substack{v \in N(1) \\ v \neq n}} \frac{g(v)}{d(v)}$$
2. **中途节点**($u \neq 1$ 且 $u \neq n$):
$$g(u) = \sum_{\substack{v \in N(u) \\ v \neq n}} \frac{g(v)}{d(v)}$$
3. **终点固定**:$g(n) = 1$(到达即终止)
> **注**:$N(u)$ 是 $u$ 的邻居集合,$d(v)$ 是节点 $v$ 的度数。终点 $n$ 不向任何节点转移[^3]。
#### 步骤 2:构建线性方程组
将 $g(1)$ 到 $g(n-1)$ 的方程整理为 $n-1$ 元线性方程组:
$$\begin{cases}
g(1) - \sum_{v \in N(1) \backslash \{n\}} \frac{g(v)}{d(v)} = 1 \\
g(u) - \sum_{v \in N(u) \backslash \{n\}} \frac{g(v)}{d(v)} = 0 & (2 \leq u \leq n-1)
\end{cases}$$
**矩阵形式示例**(以 3 节点链 1-2-3 为例):
$$\begin{bmatrix}
1 & -\frac{1}{d(2)} \\
-\frac{1}{d(1)} & 1
\end{bmatrix}
\begin{bmatrix}
g(1) \\
g(2)
\end{bmatrix} =
\begin{bmatrix}
1 \\
0
\end{bmatrix}$$
#### 步骤 3:计算边的期望经过次数
对边 $e = (u, v)$:
$$f(e) = \frac{g(u)}{d(u)} \cdot [u \neq n] + \frac{g(v)}{d(v)} \cdot [v \neq n]$$
其中 $[P]$ 是指示函数(条件 $P$ 成立时为 1,否则为 0)[^3]。
#### 步骤 4:最小化期望总和
1. 将边按 $f(e)$ **从大到小**排序
2. 分配编号:$f(e)$ 最大的边编号为 1,次大的为 2,...,最小的为 $m$
3. 总期望最小值:
$$E_{\min} = \sum_{i=1}^{m} i \cdot f(e_{\text{rank } i})$$
> **数学原理**:排序不等式(逆序和最小)[^1]。
---
### 算法流程
```mermaid
graph TD
A[输入图结构] --> B[构建线性方程组]
B --> C[高斯消元解 g1~gn-1]
C --> D[计算边期望 f(e)]
D --> E[按 f(e) 降序排序]
E --> F[分配编号 1~m]
F --> G[计算总期望 E_min]
```
#### 复杂度分析
- **高斯消元**:$O(n^3)$($n \leq 500$ 可接受)
- **边处理**:$O(m \log m)$(排序)
---
### 关键公式推导
#### 1. 节点期望方程推导
设 $p(u)$ 为访问节点 $u$ 的概率,则期望次数 $g(u) = \sum p(\text{路径经过 } u)$。由全期望公式:
- 起点 $u=1$ 初始贡献 $+1$
- 其他节点 $u$ 的访问来自邻居转移:
$$g(u) = [u=1] + \sum_{\substack{v \to u \\ v \neq n}} \overbrace{\frac{1}{d(v)}}^{\text{转移概率}} \cdot \overbrace{g(v)}^{\text{访问 } v \text{ 的期望}}$$
#### 2. 边期望公式推导
边 $(u,v)$ 被经过的两种情况:
1. $u \to v$:概率 $\frac{g(u)}{d(u)}$($u \neq n$)
2. $v \to u$:概率 $\frac{g(v)}{d(v)}$($v \neq n$)
故 $f(e) = \frac{g(u)}{d(u)} \cdot [u \neq n] + \frac{g(v)}{d(v)} \cdot [v \neq n]$
#### 3. 最小化策略证明
由排序不等式:
$$\sum_{i=1}^m a_i b_i \geq \sum_{i=1}^m a_i b_{\sigma(i)} \quad (a,b \text{ 同序时最小})$$
当 $a_i = f(e_i)$(降序)且 $b_i = i$(升序)时和最小[^1]。
---
### 代码实现要点
```cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
vector<int> G[N]; // 邻接表
int deg[N], u[N*N], v[N*N]; // 度数 & 边信息
double A[N][N], g[N]; // 方程组 & 解
void gauss(int n) { // 解 n 阶方程组
for (int i = 1; i <= n; ++i) {
int pivot = i;
for (int j = i+1; j <= n; ++j)
if (fabs(A[j][i]) > fabs(A[pivot][i])) pivot = j;
swap(A[i], A[pivot]);
for (int j = n+1; j >= i; --j) A[i][j] /= A[i][i];
for (int j = 1; j <= n; ++j) {
if (j == i) continue;
double factor = A[j][i];
for (int k = i; k <= n+1; ++k)
A[j][k] -= factor * A[i][k];
}
}
for (int i = 1; i <= n; ++i) g[i] = A[i][n+1];
}
int main() {
// 读图 & 初始化度数
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> u[i] >> v[i];
G[u[i]].push_back(v[i]);
G[v[i]].push_back(u[i]);
deg[u[i]]++, deg[v[i]]++;
}
// 构建方程组 (n-1 个未知数)
for (int i = 1; i < n; ++i) {
A[i][i] = 1.0;
for (int v : G[i]) {
if (v == n) continue; // 跳过终点
A[i][v] = -1.0 / deg[v];
}
A[i][n+1] = (i == 1) ? 1.0 : 0.0; // 常数项
}
gauss(n-1); // 解 g[1]~g[n-1]
g[n] = 1.0; // 固定终点期望
// 计算边期望
vector<double> f;
for (int i = 1; i <= m; ++i) {
double cnt = 0;
if (u[i] != n) cnt += g[u[i]] / deg[u[i]];
if (v[i] != n) cnt += g[v[i]] / deg[v[i]];
f.push_back(cnt);
}
// 排序 & 计算最小期望
sort(f.rbegin(), f.rend());
double ans = 0;
for (int i = 0; i < m; ++i)
ans += (i + 1) * f[i]; // 编号 1~m 分配
printf("%.3f\n", ans);
return 0;
}
```
> **精度提示**:高斯消元需使用部分主元法(`pivot`)避免除零和精度损失[^2]。
---
### 相关思考
1. **为什么终点 $g(n)=1$?**
游走必以概率 1 到达 $n$,故期望次数为 1[^3]。
2. **如何处理自环/重边?**
题目保证简单图,无需特殊处理[^4]。
3. **非连通图的影响?**
题目限定连通图,确保从 1 可到 $n$。
---