P3232[HNOI2013]游走
题意
给定一个 nnn 个点 mmm 条边的无向连通图,顶点从 111 编号到 nnn,边从 111 编号到 mmm。
小 Z 在该图上进行随机游走,初始时小 Z 在 111 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 nnn 号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这 mmm 条边进行编号,使得小 Z 获得的总分的期望值最小。
思路
因为路径中有环,所以不能直接递推,所以要先把期望的式子列出来,再进行高斯消元求解,求完解后,按期望大小进行排序,期望次数 大的就有小的标号,小的用大的标号,依次后求出答案
代码
#include<bits/stdc++.h> using namespace std; int du[503],num,head[503]; double b[503],f[250004]; double g[505][505],x[503]; struct{ int ne,to,w; }a[250004]; void lian(int from,int to){ num++; a[num].ne=head[from]; a[num].to=to; head[from]=num; } void guss(int n){ int i,j,k,Max; double p; for(i=1;i<=n;i++){ Max=i; for(j=i+1;j<=n;j++) if(fabs(g[Max][i])<fabs(g[j][i]))Max=j; for(j=i;j<=n;j++) swap(g[i][j],g[Max][j]); swap(b[i],b[Max]); for(j=1;j<=n;j++)if(i!=j&&g[j][i]!=0){ p=g[j][i]/g[i][i];b[j]-=b[i]*p; for(k=1;k<=n;k++) g[j][k]-=g[i][k]*p; } } } int main(){ int n,m,to,i,j,u,v,w,from; double ans=0; scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d",&u,&v); lian(u,v);lian(v,u); du[u]++;du[v]++; } for(i=1;i<n;i++){ g[i][i]=1; for(j=head[i];j;j=a[j].ne){ to=a[j].to; if(to!=n)g[i][to]=-1.0/du[to]; } }b[1]=1; guss(n-1); for(i=1;i<n;i++){ if(g[i][i]!=0)x[i]=b[i]/g[i][i]; // cout<<du[i]<<" "; } // for(i=1;i<=n;i++) // for(j=1;j<=n;j++)cout<<g[i][j]<<" "; for(i=2;i<=num;i+=2){ to=a[i].to;from=a[i-1].to; if(to!=n)f[i/2]=x[to]/du[to]; if(from!=n)f[i/2]+=x[from]/du[from]; } sort(f+1,f+num/2+1); for(i=1;i<=num/2;i++) ans+=f[i]*(num/2-i+1); printf("%.3lf\n",ans); return 0; }