题意:
一个无向连通图,顶点从1编号到N,边从1编号到M。 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
题解:
被抛硬币吓傻,赶紧来做这道简答点的。
考虑怎么算一个点经过次数期望
xi
x
i
,一条边经过次数期望
yi
y
i
,
设
di
d
i
表示
i
i
的出度。
xi=∑(i,j)∈Exjdj(1<i<n)
x
i
=
∑
(
i
,
j
)
∈
E
x
j
d
j
(
1
<
i
<
n
)
yi=xudu+xvdv
y
i
=
x
u
d
u
+
x
v
d
v
根据前两条柿子,可以列出关于 xi x i 的方程。
高斯消元解即可,由此得到 yi y i
因为要期望最小,而边的序号是不影响经过期望的,所以从小到大贪心就好了。
code:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const long double eps=1e-10;
int n,m,d[510];
struct node{
int x,y,next;
}e[250010],b[500010];
int len=0,last[510];
long double a[510][510],dis[250010];
void ins(int x,int y)
{
b[++len].y=y;
b[len].next=last[x];last[x]=len;
}
void guass()
{
for(int i=1;i<n;i++)
{
if(abs(a[i][i])<=eps)
{
for(int j=i+1;j<n;j++)
if(abs(a[j][i])>eps)
{
for(int k=i;k<=n+1;k++) swap(a[j][k],a[i][k]);
break;
}
}
for(int j=i+1;j<n;j++)
if(abs(a[j][i])>eps)
{
long 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-1;i;i--)
{
for(int j=i+1;j<=n;j++) a[i][n+1]-=a[i][j]*a[j][n+1];
a[i][n+1]/=a[i][i];
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&e[i].x,&e[i].y);d[e[i].x]++;d[e[i].y]++;
ins(e[i].x,e[i].y);ins(e[i].y,e[i].x);
}
a[n][n+1]=a[n][n]=1.0;a[1][n+1]=1.0;
for(int x=1;x<n;x++)
{
a[x][x]=1.0;
for(int i=last[x];i;i=b[i].next)
{
int y=b[i].y;
if(y!=n) a[x][y]-=(1.0/(long double)d[y]);
}
}
guass();
for(int i=1;i<=m;i++)
{
dis[i]=0.0;
if(e[i].x!=n) dis[i]+=a[e[i].x][n+1]/(long double)(d[e[i].x]);
if(e[i].y!=n) dis[i]+=a[e[i].y][n+1]/(long double)(d[e[i].y]);
}
sort(dis+1,dis+1+m);
long double ans=0.0;
for(int i=m;i;i--)
ans+=dis[m-i+1]*(long double)(i);
printf("%.3Lf",ans);
}