P3232 [HNOI2013]遊走 解題報告
阿新 • • 發佈:2019-01-12
無向連通圖 line 最小 clu 結束 數據 解題報告 ret 正整數 之間存在一條邊。
P3232 [HNOI2013]遊走
題目描述
一個無向連通圖,頂點從\(1\)編號到\(N\),邊從\(1\)編號到\(M\)。 小Z在該圖上進行隨機遊走,初始時小Z在1號頂點,每一步小Z以相等的概率隨機選 擇當前頂點的某條邊,沿著這條邊走到下一個頂點,獲得等於這條邊的編號的分數。當小Z 到達\(N\)號頂點時遊走結束,總分為所有獲得的分數之和。 現在,請你對這M條邊進行編號,使得小Z獲得的總分的期望值最小。
輸入輸出格式
輸入格式:
第一行是正整數\(N\)和\(M\),分別表示該圖的頂點數和邊數,接下來\(M\)行每行是整數\(u,v(1\le u,v\le N)\),表示頂點\(u\)與頂點\(v\)
輸入保證\(30\%\)的數據滿足\(N\le 10\),\(100\%\)的數據滿足\(2\le N\le 500\)且是一個無向簡單連通圖。
輸出格式:
僅包含一個實數,表示最小的期望值,保留3位小數。
\(f_i\)代表\(i\)這個點的期望經過次數,\(d_i\)表示度數
\[
f_v=\sum \frac{f_u}{d_u}
\]
1號點的方程常數加1,代表它原來就有1的次數,n號點不被轉移走
然後求每條邊的期望經過次數
\[
E_{u,v}=\frac{f_u}{d_u}+\frac{f_v}{d_v}
\]
然後對邊的期望次數排序,貪心匹配即可。
Code:
#include <cstdio> #include <algorithm> #include <cmath> const int N=520; int head[N],to[N*N],Next[N*N],cnt; void add(int u,int v) { to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt; } int n,m,eu[N*N],ev[N*N],in[N]; double a[N][N],ct[N*N]; void Gauss() { for(int i=1;i<=n;i++) { int id=i; for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[id][i])) id=j; std::swap(a[id],a[i]); for(int j=n+1;j>=i;j--) a[i][j]/=a[i][i]; for(int j=i+1;j<=n;j++) for(int k=n+1;k>=i;k--) a[j][k]-=a[i][k]*a[j][i]; } for(int i=n;i;i--) for(int j=i-1;j;j--) a[j][n+1]-=a[i][n+1]*a[j][i]; } int main() { scanf("%d%d",&n,&m); for(int u,v,i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v),add(v,u); ++in[u],++in[v]; eu[i]=u,ev[i]=v; } a[1][n+1]=1; for(int u=1;u<=n;u++) { a[u][u]=1; for(int i=head[u];i;i=Next[i]) if(to[i]!=n) a[u][to[i]]=-1.0/in[to[i]]; } Gauss(); for(int i=1;i<=m;i++) { if(eu[i]!=n) ct[i]=a[eu[i]][n+1]/in[eu[i]]; if(ev[i]!=n) ct[i]+=a[ev[i]][n+1]/in[ev[i]]; } std::sort(ct+1,ct+1+m); double ans=0; for(int i=1;i<=m;i++) ans+=ct[i]*(m+1-i); printf("%.3f\n",ans); return 0; }
2019.1.12
P3232 [HNOI2013]遊走 解題報告