BZOJ3143 [HNOI2013]遊走
阿新 • • 發佈:2019-01-10
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 32 3
1 2
1 3
Sample Output
3.333HINT
邊(1,2)編號為1,邊(1,3)編號2,邊(2,3)編號為3。權當複習高斯消元嘍 如果我們能求出每條邊被經過的期望次數,那麼只要從大到小標號就好了 而求邊是比較難的,相對的求點更簡單。。。 而一條邊被經過的次數可以由兩個端點算出 現在只需要求點即可,我們發現每個點的期望都跟所有跟他相鄰的點有關 也就是說我們能列出n個方程,接下來就要用高斯消元來處理 f就是那個矩陣,因為n比較特殊所以就作為存等式右邊的一列,n*(n-1) 首先1號點在開始時必須被經過1次,f[1][n]+=1 然後在各項填上係數,使得f[i][n]=0 之後就是消元 在這裡是將f[j][i]絕對值最大的那一行換到第i行上 接下來再把f[i][i]調整成1,在所有第i列上不為0的行上消去第i列 這樣做結束後直接f[i][n]就是i的答案 最後統計邊,標號,輸出答案
1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #define N 505 5 #define eps 1e-8 6 using namespace std; 7 int n,m; 8 int to[N*N*2],nxt[N*N*2],h[N],etop; 9 int F[N*N],T[N*N]; 10 double q[N*N]; 11 int num[N]; 12 double f[N][N]; 13 double im[N*N*2]; 14 voidView Codeadd(int u,int v){ 15 num[u]++;to[++etop]=v; 16 nxt[etop]=h[u];h[u]=etop; 17 } 18 int main(){ 19 scanf("%d%d",&n,&m); 20 for(int i=1,u,v;i<=m;i++){ 21 scanf("%d%d",&u,&v); 22 add(u,v);add(v,u); 23 F[i]=u;T[i]=v; 24 } 25 f[1][n]=1; 26 for(int i=1;i<n;i++){ 27 f[i][i]=1; 28 for(int j=h[i];j;j=nxt[j]) 29 if(to[j]!=n) 30 f[i][to[j]]=-1.0/num[to[j]]; 31 } 32 for(int i=1;i<n;i++){ 33 int now=i; 34 double s=f[i][i]; 35 for(int j=i+1;j<n;j++) 36 if(fabs(f[j][i])-fabs(s)>eps){now=j;s=f[j][i];} 37 if(now!=i){ 38 for(int j=1;j<n;j++) 39 swap(f[i][j],f[now][j]); 40 } 41 for(int j=n;j>=i;j--)f[i][j]/=f[i][i]; 42 for(int j=1;j<n;j++) 43 if(i!=j) 44 for(int k=n;k>=i;k--) 45 f[j][k]-=f[j][i]*f[i][k]; 46 } 47 for(int i=1;i<=m;i++){ 48 if(F[i]!=n) 49 q[i]+=f[F[i]][n]*(1.0/num[F[i]]); 50 if(T[i]!=n) 51 q[i]+=f[T[i]][n]*(1.0/num[T[i]]); 52 } 53 sort(q+1,q+1+m); 54 double ans=0; 55 for(int i=1;i<=m;i++) 56 ans+=q[i]*((m-i+1)*1.0); 57 printf("%.3lf",ans); 58 return 0; 59 }