BZOJ 3143 游走(高斯消元)
题意:一个无向连通图,顶点从1编号到n,边从1编号到m。小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达n号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这m条边进行编号,使得小Z获得的总分的期望值最小。
思路:显然,需要求出每条边的期望经过次数,然后排序贪心赋值即可,但是每条边的期望经过次数是什么呢?
是 E(e)=E(u)/D(u) + E(v)/D(v) (u,v∈e)
所以做法就是先求出每个点的期望经过次数,还有,就是对于第n个点,它的期望在最后统计的时候要看做0,因为到了n点就不会再出来了。
1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 struct edge{ 7 int u,v; 8 double w; 9 }e[500005]; 10 int n,m,du[500005],go[1005][1005]; 11 double a[1010][1010],p[500005]; 12 bool Cmp(edge q,edge w){ 13 return q.w>w.w; 14 } 15 void gauss(){ 16 int to,now=1; 17 for (int i=1;i<=n;i++){ 18 for (to=now;to<=n;to++) if (a[to][i]!=0) break; 19 if (to>n) continue; 20 if (to!=now) for (int j=1;j<=n+1;j++) std::swap(a[now][j],a[to][j]); 21 double t=a[now][i]; 22 for (int j=1;j<=n+1;j++) a[now][j]/=t; 23 for (int j=1;j<=n;j++) 24 if (j!=now){ 25 t=a[j][i]; 26 for (int k=1;k<=n+1;k++) 27 a[j][k]-=t*a[now][k]; 28 } 29 now++; 30 } 31 } 32 int main(){ 33 scanf("%d%d",&n,&m); 34 int tot=0; 35 for (int i=1;i<=m;i++){ 36 int x,y; 37 scanf("%d%d",&x,&y); 38 if (x==y) continue; 39 e[++tot].u=x;e[tot].v=y; 40 go[x][++go[x][0]]=y; 41 go[y][++go[y][0]]=x; 42 du[e[tot].u]++; 43 du[e[tot].v]++; 44 } 45 m=tot; 46 for (int i=1;i<n;i++) a[i][i]=1; 47 for (int i=1;i<n;i++){ 48 for (int j=1;j<=go[i][0];j++){ 49 int t=go[i][j]; 50 if (t==n) continue; 51 a[i][t]-=1.0/du[t]; 52 } 53 } 54 n--; 55 a[1][n+1]=1; 56 gauss(); 57 for (int i=1;i<=n;i++) p[i]=a[i][n+1]; 58 for (int i=1;i<=m;i++) 59 e[i].w=((double)p[e[i].u])/((double)du[e[i].u])+((double)p[e[i].v])/((double)du[e[i].v]); 60 std::sort(e+1,e+1+m,Cmp); 61 double ans=0; 62 for (int i=1;i<=m;i++) ans+=e[i].w*i; 63 printf("%.3f\n",ans); 64 }