problem
一个无向连通图,顶点从 1 1 1 编号到 n n n,边从 1 1 1 编号到 m m m。
小 Z 在该图上进行随机游走,初始时小 Z 在 1 1 1 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 n n n 号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这 m m m 条边进行编号,使得小 Z 获得的总分的期望值最小。
数据范围: 2 ≤ n ≤ 500 2\le n\le500 2≤n≤500,保证图是一个简单无向连通图。
solution
根据期望的线性性,我们可以先算出经过的边的期望,再分配标号。
但是边的期望不好算,我们可以转换一下:
E [ ( u , v ) ] = E [ u ] d e g u + E [ v ] d e g v E[(u,v)]=\frac{E[u]}{\mathrm{deg}_u}+\frac{E[v]}{\mathrm{deg}_v} E[(u,v)]=deguE[u]+degvE[v]
其中 d e g i \mathrm{deg}_i degi 表示 i i i 的度数。
那么,我们就把求边的期望转化成了求点的期望,那么有:
E [ u ] = ∑ u → v E [ v ] d e g v E[u]=\sum_{u\rightarrow v}\frac{E[v]}{\mathrm{deg}_v} E[u]=u→v∑degvE[v]
其中 u → v u\rightarrow v u→v 表示 u , v u,v u,v 间有一条连边。
那么我们列出 n n n 个方程之后高斯消元即可解除所有的 E [ u ] E[u] E[u]。
注意要两个点是特殊的:
- 1 1 1 是起点,一开始就会有 1 1 1 的期望。
- n n n 是终点,走到 n n n 就不会继续走了,因此不能在其他方程中列出。
求出所有边的期望后,根据排序不等式,给期望更大的边更小的编号得到的最后的期望是最小的。
时间复杂度 O ( n 3 ) O(n^3) O(n3)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=505,M=5e5+5;
int n,m,deg[N];
struct edges{int u,v;double P;}E[M];
bool operator<(const edges &p,const edges &q) {return p.P>q.P;}
double a[N][N],X[N];
void Guass(){
for(int i=1;i<=n;++i){
int k=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[j][i])>fabs(a[k][i])) k=j;
swap(a[i],a[k]);
for(int j=i+1;j<=n;++j)
for(int k=i+1;k<=n+1;++k)
a[j][k]-=a[j][i]*a[i][k]/a[i][i];
}
for(int i=n;i>=1;--i){
for(int j=i+1;j<=n;++j)
a[i][n+1]-=X[j]*a[i][j];
X[i]=a[i][n+1]/a[i][i];
}
}
vector<int>e[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y),E[i]=(edges){x,y};
deg[x]++,deg[y]++,e[x].push_back(y),e[y].push_back(x);
}
for(int i=1;i<n;++i){
a[i][i]=1,a[i][n+1]=0;
for(int &to:e[i]) if(to!=n) a[i][to]=-1.0/deg[to];
}
a[1][n+1]=1,a[n][n]=1;
Guass();
for(int i=1;i<=m;++i){
int u=E[i].u,v=E[i].v;
E[i].P=X[u]/deg[u]+X[v]/deg[v];
}
sort(E+1,E+m+1);
double ans=0;
for(int i=1;i<=m;++i) ans+=E[i].P*i;
printf("%.3lf\n",ans);
return 0;
}