题目简述:
给一个无向图,小Z从一到N随机游走获得分数,每条边对分数的贡献是其编号,求总分期望的最小值,复杂度即可。
解析:
贪心的话可以想到应该让期望小的边配编号大的边,那么我们要做的是就是处理出边的期望值。
考虑一条边一定是有他的两个顶点转移过来的,所以边的期望=两顶点的期望和。每一个点的期望又可以从它相邻的所有点转移过来,设这个点是x,与它相邻的点是 i ;(别忘了乘概率)
接下来的操作就很秀了:高斯消元解方程。这里如果列 n 组,铁定是解不出来的,但是注意到:小Z在N点结束游走,所以这里的方程只有n-1组,加上N点的数据刚刚好解出来。
#include<bits/stdc++.h>
using namespace std;
int n,m,tot,du[510],head[510];
int u[1250010],v[1250010];
struct node{int to,next;}e[1250010];
void add_edge(int u,int v)
{
e[++tot].to=v;
e[tot].next=head[u];
head[u]=tot;
}
double a[510][510],b[510],x[510],f[1250010],ans;
void gauss(int n)
{
for(int i=1;i<=n;i++)
{
int maxx=i;
for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[maxx][i])) maxx=j;
if(i!=maxx) swap(a[i],a[maxx]),swap(b[i],b[maxx]);
for(int j=i+1;j<=n;j++)
{
double tmp=a[j][i]/a[i][i];
b[j]-=tmp*b[i];
for(int k=i;k<=n;k++) a[j][k]-=tmp*a[i][k];
}
}
for(int i=n;i;i--)
{
for(int j=i+1;j<=n;j++) b[i]-=x[j]*a[i][j];
x[i]=b[i]/a[i][i];
}
}
signed main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
add_edge(u[i],v[i]);
add_edge(v[i],u[i]);
du[u[i]]++;
du[v[i]]++;
}
for(int uu=1;uu<n;uu++)
{
a[uu][uu]=1;
for(int j=head[uu];j;j=e[j].next)
{
int vv=e[j].to;
if(vv!=n) a[uu][vv]=-1.0/du[vv];
}
}
b[1]=1;
gauss(n-1);
//f[x]=sum(f[i]/num[i]);
for(int i=1;i<=m;i++)
{
if(u[i]!=n) f[i]+=1.0*x[u[i]]/du[u[i]];
if(v[i]!=n) f[i]+=1.0*x[v[i]]/du[v[i]];
}
sort(f+1,f+m+1);//小期望对应大编号(贪心)
for(int i=1;i<=m;i++) ans+=(m-i+1)*f[i];
printf("%.3lf",ans);
return 0;
}