1. 程式人生 > >BZOJ3143:[Hnoi2013]遊走——題解

BZOJ3143:[Hnoi2013]遊走——題解

ostream output ref 但是 概率 ont 最小 lock 我們

http://www.lydsy.com/JudgeOnline/problem.php?id=3143

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

Sample Output

3.333

參考http://blog.csdn.net/vmurder/article/details/44542575

期望不會啊怎麽辦……

首先我們很容易想到一個貪心:將所有的邊走過的期望求出,大期望配小編號即可。

那麽求邊的期望w,它的左右端點為u和v,度分別為d[u],d[v],走過點的期望為x[u],x[v],那麽顯然:

w=x[u]/d[u]+x[v]/d[v]。

現在變成了求x,顯然就是與它相鄰的點的(期望/度數)之和。

但是x1顯然需要在原有的基礎上+1(因為從該點出發必然經過一次)

xn顯然是0(雖然是有且僅有一次會進去,所以理論上應當為1,但是一旦進去就出不來了,對於所有其他的運算來說xn即為0,那我們更新的時候直接把他當做0走即可)

剩下的就是高斯消元了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using
namespace std; typedef double dl; const int N=505; const int M=N*N; int u[M],v[M],d[N]; dl f[N][N],x[N],w[M]; inline void Gauss(int n,int m){ for(int i=1;i<=n;i++){ int l=i; for(int j=l+1;j<=n;j++) if(fabs(f[l][i])<fabs(f[j][i]))l=j; if(l!=i) for(int j=i;j<=m;j++) swap(f[l][j],f[i][j]); for(int j=i+1;j<=n;j++){ dl temp=f[j][i]/f[i][i]; for(int k=i;k<=m;k++) f[j][k]=f[j][k]-f[i][k]*temp; } } for(int i=n;i>=1;i--){ dl t=f[i][m]; for(int j=n;j>i;j--) t-=x[j]*f[i][j]; x[i]=t/f[i][i]; } return ; } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&u[i],&v[i]); d[u[i]]++;d[v[i]]++; } for(int i=1;i<n;i++)f[i][i]=-1; for(int i=1;i<=m;i++){ f[u[i]][v[i]]+=1.0/d[v[i]]; f[v[i]][u[i]]+=1.0/d[u[i]]; } for(int i=1;i<=n;i++)f[n][i]=0; f[1][n+1]=-1,f[n][n]=1; Gauss(n,n+1); for(int i=1;i<=m;i++)w[i]=x[u[i]]/d[u[i]]+x[v[i]]/d[v[i]]; sort(w+1,w+m+1); dl ans=0; for(int i=1;i<=m;i++)ans+=(m-i+1)*w[i]; printf("%.3f\n",ans); return 0; }

BZOJ3143:[Hnoi2013]遊走——題解