题意:给定一个 个点 条边的无向连通图。在该图上进行随机游走,起点为 号顶点,每一步以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数,到达 号顶点时游走结束,总分为所有获得的分数之和。对这 条边进行编号,使获得的总分的期望值最小。
首先我们不难发现要让总分的期望值最小,期望经过次数越多的边的编号越小,所以我们要求的就是所有边的期望经过次数。
设点的期望经过次数为
,该点的度数为
,每一条边的期望经过次数为
。
我们可以根据点的期望求出边的期望。可以推出:
因为d数组我们可以在读入时处理好,那么点的期望经过次数应该怎么求呢?
对于每一个点,
,根据这个式子,我们对于每一个点列一个方程。
是这个方程组的增广矩阵。每一行的的第
列就是
的值,首先我们通过移项把第
列变成
,所以它的系数从
变为
。
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;
在此引用forever_shi神犇的解释
对于
号点,
值是
,但是我们在实际操作时把第
行只有
的系数设为
,其他的所有系数都设为
,包括增广的那一列也是
。但是这样我们会发现最后一个式子成了
。
这样做是因为我们在考虑每个点给每条边的影响时,
号点是不会对与它相连的边产生贡献的,因为到了
号点就不能再返回了,所以为了正确地统计边的期望经过次数,强制将点
的期望经过次数设为了
,实际上应该是
的。而把
也就是
的系数设为1的原因是为了避免在做
个元的高斯消元时
时因为除以
而
。
完成了以上步骤,我们就可以进行高斯消元了,解出来每一个点的 的值,根据
这个公式求出每一条边的期望经过次数。
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;
}