1. 程式人生 > >bzoj 3143 [Hnoi2013]遊走 期望dp+高斯消元

bzoj 3143 [Hnoi2013]遊走 期望dp+高斯消元

ace 保留 sca earch algorithm 整數 include 上進 連通

[Hnoi2013]遊走

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 3394 Solved: 1493
[Submit][Status][Discuss]

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

HINT

邊(1,2)編號為1,邊(1,3)編號2,邊(2,3)編號為3。

Source

非官方數據

題解:   很好想的,列出期望方程,對於每個點,都有一個方程,n-1個方程   解n-1個元,g[i]表示到達終點還需要多少。   即可,發現一條邊,只會在到u或者v產生貢獻。
 1 #include<cmath>
 2
#include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define N 507 7 #define M 250007 8 9 using namespace std; 10 11 int n,m; 12 int U[M],V[M],d[N]; 13 double a[N][N],x[N],w[M],ans; 14 15 void Gauss(int n,int m) 16 { 17 int
i,j,k; 18 for(i=1;i<m;i++) 19 { 20 for(k=i,j=i+1;j<=n;j++)if(fabs(a[k][i])<fabs(a[j][i]))k=j; 21 if(i!=k)for(j=i;j<=m;j++)swap(a[i][j],a[k][j]); 22 for(j=i+1;j<=n;j++) 23 { 24 double rate=a[j][i]/a[i][i]; 25 for(k=i;k<=m;k++)a[j][k]-=a[i][k]*rate; 26 } 27 } 28 for(i=m-1;i;i--) 29 { 30 for(j=i+1;j<m;j++)a[i][m]-=a[i][j]*x[j]; 31 x[i]=a[i][m]/a[i][i]; 32 } 33 } 34 int main() 35 { 36 int i; 37 38 scanf("%d%d",&n,&m); 39 for(i=1;i<=m;i++) 40 { 41 scanf("%d%d",&U[i],&V[i]); 42 d[U[i]]++,d[V[i]]++; 43 } 44 for(i=1;i<n;i++)a[i][i]=-1; 45 for(i=1;i<=m;i++) 46 { 47 a[U[i]][V[i]]+=1.0/d[V[i]]; 48 a[V[i]][U[i]]+=1.0/d[U[i]]; 49 } 50 for(i=1;i<=n;i++)a[n][i]=0; 51 a[1][n+1]=-1,a[n][n]=1; 52 Gauss(n,n+1); 53 for(i=1;i<=m;i++)w[i]=x[U[i]]/d[U[i]]+x[V[i]]/d[V[i]]; 54 sort(w+1,w+m+1); 55 for(i=1;i<=m;i++)ans+=(m-i+1)*w[i]; 56 printf("%.3lf\n",ans); 57 }

bzoj 3143 [Hnoi2013]遊走 期望dp+高斯消元