题目链接
给出一个
n
n
n个点
m
m
m条边的无向图,从
1
1
1号节点出发,每次等概率选择一条边移动至相邻节点,到
n
n
n点时结束。给出每条边的边权为该条边的编号,存在一种边的编号方案,使得该非简单路径边权和的期望最小,求出该期望值。
不论我们如何编号,每条边的期望经过次数是不会变的,要使得边权和的期望最小,只需要贪心地使期望次数和边权倒序对应即可。
考虑如何求每条边的经过次数,记每个点度数为
d
i
d_i
di,期望通过次数为
x
i
x_i
xi,每条边期望通过次数为
y
i
y_i
yi则
x
n
=
1
,
x
1
=
1
+
∑
(
1
,
j
)
∈
E
x
j
d
j
,
x
i
=
∑
(
i
,
j
)
∈
E
x
j
d
j
(
1
<
i
<
n
)
,
y
i
=
x
u
i
d
u
i
+
x
v
i
d
v
i
x_n=1,x_1=1+\sum_{(1,j)\in E}\frac{x_j}{d_j},x_i=\sum_{(i,j)\in E}\frac{x_j}{d_j}(1<i<n),y_i=\frac{x_{u_i}}{d_{u_i}}+\frac{x_{v_i}}{d_{v_i}}
xn=1,x1=1+(1,j)∈E∑djxj,xi=(i,j)∈E∑djxj(1<i<n),yi=duixui+dvixvi
变形化简一下高斯消元就可以了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 550, M = N*N/2;
long double a[N][N], x[N], y[N];
int n, m, d[N];
struct edge {
int u, v;
long double t;
inline void init() {
scanf("%d%d", &u, &v); ++d[u], ++d[v];
}
inline void calc() {
t = x[u] / d[u] + x[v] / d[v];
}
inline bool operator < (const edge &rhs) const {
return t < rhs.t;
}
}lib[M];
inline void work() {
for (int i = 1; i <= n; i++) a[i][i] = 1;
y[1] = 1;
for (int i = 1; i <= m; i++) {
int u = lib[i].u, v = lib[i].v;
a[u][v] = -1.0 / (long double)d[v];
a[v][u] = -1.0 / (long double)d[u];
}
n--;
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) {
long double lamda = - a[j][i] / a[i][i]; a[j][i] = 0; y[j] += y[i] * lamda;
for (int k = i + 1; k <= n; k++)
a[j][k] += a[i][k] * lamda;
}
for (int i = n; i >= 1; i--) {
for (int j = i + 1; j <= n; j++)
y[i] -= a[i][j] * x[j];
x[i] = y[i] / a[i][i];
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) lib[i].init();
work();
for (int i = 1; i <= m; i++) lib[i].calc();
sort(lib + 1, lib + 1 + m);
long double ans = 0;
for (int i = 1; i <= m; i++) ans += lib[i].t * (m + 1 - i);
printf("%.3Lf\n", ans);
return 0;
}