题意:给定一个 n n 个点条边的无向连通图。在该图上进行随机游走,起点为 1 1 号顶点,每一步以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数,到达号顶点时游走结束,总分为所有获得的分数之和。对这 m m 条边进行编号,使获得的总分的期望值最小。
首先我们不难发现要让总分的期望值最小,期望经过次数越多的边的编号越小,所以我们要求的就是所有边的期望经过次数。
设点的期望经过次数为,该点的度数为
d[i]
d
[
i
]
,每一条边的期望经过次数为
b[i]
b
[
i
]
。
我们可以根据点的期望求出边的期望。可以推出:
因为d数组我们可以在读入时处理好,那么点的期望经过次数应该怎么求呢?
对于每一个点,
f[i]=∑u,v之间有边u f[j]d[j]
f
[
i
]
=
∑
u
u
,
v
之
间
有
边
f
[
j
]
d
[
j
]
,根据这个式子,我们对于每一个点列一个方程。
a[][]
a
[
]
[
]
是这个方程组的增广矩阵。每一行的的第
n+1
n
+
1
列就是
f[i]
f
[
i
]
的值,首先我们通过移项把第
n+1
n
+
1
列变成
0
0
,所以它的系数从变为
−1
−
1
。
for(int i=1;i<=n;++i)
a[i][i]=-1;
然后我们根据式子 f[i]=∑u,v之间有边u f[j]d[j] f [ i ] = ∑ u u , v 之 间 有 边 f [ j ] d [ j ] 将各项的系数也就是 1d[j] 1 d [ j ] 累加入 a a 数组。
for(int i=1;i<=m;++i)
{
a[e[i].from][e[i].to]+=1.0/d[e[i].to];
a[e[i].to][e[i].from]+=1.0/d[e[i].from];
}
接下来的对一些初值进行解释说明
for(int i=1;i<=n+1;++i)
a[n][i]=0;
a[n][n]=1;
a[1][n+1]=-1;
在此引用forever_shi神犇的解释
对于号点,
f[n]
f
[
n
]
值是
1
1
,但是我们在实际操作时把第行只有
f[n]
f
[
n
]
的系数设为
1
1
,其他的所有系数都设为,包括增广的那一列也是
0
0
。但是这样我们会发现最后一个式子成了。
这样做是因为我们在考虑每个点给每条边的影响时,
n
n
号点是不会对与它相连的边产生贡献的,因为到了号点就不能再返回了,所以为了正确地统计边的期望经过次数,强制将点
n
n
的期望经过次数设为了,实际上应该是
1
1
的。而把也就是
a[n][n]
a
[
n
]
[
n
]
的系数设为1的原因是为了避免在做
n
n
个元的高斯消元时时因为除以
0
0
而。
完成了以上步骤,我们就可以进行高斯消元了,解出来每一个点的 f f 的值,根据
这个公式求出每一条边的期望经过次数。
for(int i=1;i<=m;++i)
b[i]=a[e[i].from][n+1]/d[e[i].from]+a[e[i].to][n+1]/d[e[i].to];
最后贪心地编号求解即可
下面完整代码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int next,to,from;
}e[1001000];
int head[1001000],num,n,m;
double a[2000][2000],d[1001000],b[1001000];
void add(int from,int to)
{
e[++num].next=head[from];
e[num].from=from;
e[num].to=to;
head[from]=num;
}
void gauss()
{
for(int i=1;i<=n;++i)
{
int r=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[j][i])>fabs(a[r][i]))
r=j;
if(r!=i)
swap(a[r],a[i]);
for(int j=i+1;j<=n;++j)
{
double t=a[j][i]/a[i][i];
for(int k=i;k<=n+1;++k)
a[j][k]-=t*a[i][k];
}
}
for(int i=n;i>=1;--i)
{
for(int j=n;j>i;--j)
a[i][n+1]-=a[j][n+1]*a[i][j];
a[i][n+1]/=a[i][i];
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
d[x]++;
d[y]++;
}
for(int i=1;i<=n;++i)
a[i][i]=-1;
for(int i=1;i<=m;++i)
{
a[e[i].from][e[i].to]+=1.0/d[e[i].to];
a[e[i].to][e[i].from]+=1.0/d[e[i].from];
}
for(int i=1;i<=n+1;++i)
a[n][i]=0;
a[n][n]=1;
a[1][n+1]=-1;
gauss();
for(int i=1;i<=m;++i)
b[i]=a[e[i].from][n+1]/d[e[i].from]+a[e[i].to][n+1]/d[e[i].to];
sort(b+1,b+m+1);
double ans=0.0;
for(int i=1;i<=m;++i)
ans+=b[i]*(m-i+1);
printf("%.3lf\n",ans);
return 0;
}