bzoj3143
【bzoj3143】[Hnoi2013]游走
题目描述
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
输入
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。
输出
仅包含一个实数,表示最小的期望值,保留3位小数。
样例输入
3 3
2 3
1 2
1 3
样例输出
3.333
sol:十分显然的是要让经过概率高的边权尽可能小,难点就在于求每条边被经过的概率
令一个点 x 的度数为 Degx,那么对于一条边<U,V>,被经过的概率就是(经过点U的概率*1/Deg[U]+经过点V的概率*1/Deg[V])
经过某个点的概率就是∑与它相连的点的概率/那个点的度数,这个显然是一个n个未知数,n个式子的一次方程组,可以用高斯消元求解
Ps:经过1的概率要+1,经过n的概率是0,因为到了n是不会出来的
// luogu-judger-enable-o2 #include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=505,M=500005; int n,m; int tot=0,Next[M],to[M],head[N],Deg[N]; double a[N][N],b[N]; struct Bian { int U,V; double Gl; }E[M]; inline bool cmp_Gl(Bian p,Bian q) { return p.Gl<q.Gl; } inline void add(int x,int y) { Deg[x]++; Next[++tot]=head[x]; to[tot]=y; head[x]=tot; } inline void Debug(int n) { int i,j; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) printf("%.2lf ",a[i][j]); printf("%.2lf",b[i]); putchar('\n'); } putchar('\n'); } inline void Gauss(int n) { int i,j,k; double Div; for(i=1;i<=n;i++) { int Pos=i; for(j=i+1;j<=n;j++) if(fabs(a[Pos][i])<fabs(a[j][i])) Pos=j; if(Pos!=i) { swap(a[Pos],a[i]); swap(b[Pos],b[i]); } Div=a[i][i]; for(j=i;j<=n;j++) a[i][j]/=Div; b[i]/=Div; for(j=1;j<=n;j++) if(j!=i) { double Div=a[j][i]; for(k=i;k<=n;k++) a[j][k]-=Div*a[i][k]; b[j]-=Div*b[i]; } // Debug(n); } } int main() { int i,j; R(n); R(m); for(i=1;i<=m;i++) { int x=read(),y=read(); add(x,y); add(y,x); E[i].U=x; E[i].V=y; } for(i=1;i<n;i++) { a[i][i]=1.0; for(j=head[i];j;j=Next[j]) if(to[j]!=n) { a[i][to[j]]=-1.0/Deg[to[j]]; } b[i]=(i==1)?1:0; } Gauss(n-1); for(i=1;i<=m;i++) { E[i].Gl=1.0*b[E[i].U]/Deg[E[i].U]+1.0*b[E[i].V]/Deg[E[i].V]; } sort(E+1,E+m+1,cmp_Gl); double ans=0; for(i=1;i<=m;i++) { ans+=1.00*E[i].Gl*(m-i+1); } printf("%.3lf\n",ans); return 0; } /* input 3 3 2 3 1 2 1 3 output 3.333 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!