BZOJ3143 [HNOI2013]游走
Description
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
Input
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。
Output
仅包含一个实数,表示最小的期望值,保留3位小数。
Sample Input
3 3
2 3
1 2
1 3
2 3
1 2
1 3
Sample Output
3.333
HINT
边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。
权当复习高斯消元喽
如果我们能求出每条边被经过的期望次数,那么只要从大到小标号就好了
而求边是比较难的,相对的求点更简单。。。
而一条边被经过的次数可以由两个端点算出
现在只需要求点即可,我们发现每个点的期望都跟所有跟他相邻的点有关
也就是说我们能列出n个方程,接下来就要用高斯消元来处理
f就是那个矩阵,因为n比较特殊所以就作为存等式右边的一列,n*(n-1)
首先1号点在开始时必须被经过1次,f[1][n]+=1
然后在各项填上系数,使得f[i][n]=0
之后就是消元
在这里是将f[j][i]绝对值最大的那一行换到第i行上
接下来再把f[i][i]调整成1,在所有第i列上不为0的行上消去第i列
这样做结束后直接f[i][n]就是i的答案
最后统计边,标号,输出答案
1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #define N 505 5 #define eps 1e-8 6 using namespace std; 7 int n,m; 8 int to[N*N*2],nxt[N*N*2],h[N],etop; 9 int F[N*N],T[N*N]; 10 double q[N*N]; 11 int num[N]; 12 double f[N][N]; 13 double im[N*N*2]; 14 void add(int u,int v){ 15 num[u]++;to[++etop]=v; 16 nxt[etop]=h[u];h[u]=etop; 17 } 18 int main(){ 19 scanf("%d%d",&n,&m); 20 for(int i=1,u,v;i<=m;i++){ 21 scanf("%d%d",&u,&v); 22 add(u,v);add(v,u); 23 F[i]=u;T[i]=v; 24 } 25 f[1][n]=1; 26 for(int i=1;i<n;i++){ 27 f[i][i]=1; 28 for(int j=h[i];j;j=nxt[j]) 29 if(to[j]!=n) 30 f[i][to[j]]=-1.0/num[to[j]]; 31 } 32 for(int i=1;i<n;i++){ 33 int now=i; 34 double s=f[i][i]; 35 for(int j=i+1;j<n;j++) 36 if(fabs(f[j][i])-fabs(s)>eps){now=j;s=f[j][i];} 37 if(now!=i){ 38 for(int j=1;j<n;j++) 39 swap(f[i][j],f[now][j]); 40 } 41 for(int j=n;j>=i;j--)f[i][j]/=f[i][i]; 42 for(int j=1;j<n;j++) 43 if(i!=j) 44 for(int k=n;k>=i;k--) 45 f[j][k]-=f[j][i]*f[i][k]; 46 } 47 for(int i=1;i<=m;i++){ 48 if(F[i]!=n) 49 q[i]+=f[F[i]][n]*(1.0/num[F[i]]); 50 if(T[i]!=n) 51 q[i]+=f[T[i]][n]*(1.0/num[T[i]]); 52 } 53 sort(q+1,q+1+m); 54 double ans=0; 55 for(int i=1;i<=m;i++) 56 ans+=q[i]*((m-i+1)*1.0); 57 printf("%.3lf",ans); 58 return 0; 59 }