来自FallDream的博客,未经允许,请勿转载,谢谢。
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
n<=500
用Si表示到达第i个点的期望次数,显然Si可以表达成其它的一些S的和(一号点给它加1)
然后上高斯消元 消出Si,每条边的期望次数易求 排序之后标号即可
理论复杂度n^3+mlogm 但是跑的飞快
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #define MN 500 #define ld long double #define eps 1e-13 #define getchar() (*SS++) char B[1<<26],*SS=B; using namespace std; inline int read() { int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } int n,m,head[MN+5],cnt=0,d[MN+5],cc=0; ld s[MN+5][MN+5],ex[MN+5],S[MN*MN*2],ans=0; struct edge{int to,next;}e[MN*MN*2]; inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;++d[f];} void Gauss() { register int i,j; for(i=1;i<=n;++i) { for(j=i;j<=n;++j) if(fabs(s[j][i])>eps) { if(j!=i) { for(int k=i;k<=n+1;++k) swap(s[j][k],s[i][k]); } break; } for(j=i+1;j<=n;++j) if(fabs(s[j][i])>eps) { ld change=s[j][i]/s[i][i]; for(register int k=i;k<=n+1;++k) s[j][k]=s[j][k]-s[i][k]*change; } } for(int i=n;i;--i) { ld k=s[i][n+1]; for(int j=i+1;j<=n;++j) k-=ex[j]*s[i][j]; ex[i]=k/s[i][i]; } } int main() { fread(B,1,1<<26,stdin); n=read();m=read(); for(register int i=1;i<=m;i++) { int x=read(),y=read(); ins(x,y);ins(y,x); } for(register int i=1;i<n;s[i][i]=-1,++i) for(register int j=head[i];j;j=e[j].next) s[e[j].to][i]=(ld)1/d[i]; s[n][n]=-1; s[1][n+1]=-1; Gauss();ex[n]=0; for(register int i=1;i<=n;++i) for(register int j=head[i];j;j=e[j].next) if(j&1) S[++cc]=ex[i]/d[i]+ex[e[j].to]/d[e[j].to]; sort(S+1,S+cc+1,greater<ld>()); for(int i=1;i<=cc;++i) ans+=S[i]*i; printf("%0.3lf ",(double)ans); return 0; }