1. 程式人生 > >【BZOJ】1778: [Usaco2010 Hol]Dotp 驅逐豬玀

【BZOJ】1778: [Usaco2010 Hol]Dotp 驅逐豬玀

高斯 post isp for col img 應該 直接 algorithm

【題意】給定無向圖,炸彈開始在1,在每個點爆炸概率Q=p/q,不爆炸則等概率往鄰點走,求在每個點爆炸的概率。n<=300。

【算法】概率+高斯消元

【題解】很直接的會考慮假設每個點爆炸的概率,無法轉移。每個點不爆炸的概率,也無法轉移。

因為爆炸概率相同,那麽每個點爆炸的概率應該和到達該點的概率正相關。(另一種思路是和到達次數正相關)

設f[x]表示炸彈到達點x的概率(之前不爆炸)。

考慮枚舉點x的下一步,發現無法用點y的概率來轉移(因為f[y]可能由別的路走到)。

考慮枚舉點x的上一步,根據全概率公式P(A)=P(Bi)*P(A|Bi):

$$f[x]=\sum_{y}\frac{f[y]*(1-Q)}{out[y]} \ \ , \ \ y \rightarrow x$$

理解:依賴於每一個可以到達x的點y,P(Bi)就是f[y],在到達y的前提下到達x的概率就是P(A|Bi)=(1-Q)/out[y]。

(另一種理解,依賴於每一條可以到達x的邊,P(Bi)=f[y]*(1-Q)/out[y],P(A|Bi)=1)

特別的,點1一開始到達的概率就是1,所以f[1]++

最後ans[x]=f[x]*Q。或者根據炸彈最終爆炸概率為1,算Σf[i]後均分概率。

此題還可以計算每個點到達的期望次數,也是正相關。

技術分享圖片
#include<cstdio>
#include<cstring>
#include<algorithm>
using
namespace std; const int maxn=310,maxm=50010; struct edge{int v,from;}e[maxm*2]; int first[maxn],tot,d[maxn],n,m,p,q; double f[maxn][maxn]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;d[v]++;} void gauss(){ for(int i=1;i<=n;i++){ int t=i; for(int j=i+1
;j<=n;j++)if(f[j][i]>f[t][i])t=j; if(t!=i)for(int j=i+1;j<=n;j++)swap(f[i][j],f[t][j]); for(int j=i+1;j<=n;j++) for(int k=n+1;k>=i;k--) f[j][k]-=f[j][i]/f[i][i]*f[i][k]; } for(int i=n;i>=1;i--){ for(int j=i+1;j<=n;j++)f[i][n+1]-=f[i][j]*f[j][n+1]; f[i][n+1]/=f[i][i]; } } int main(){ scanf("%d%d%d%d",&n,&m,&p,&q);double Q=1.0*p/q; int u,v; for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); insert(u,v);insert(v,u); } f[1][n+1]=1; for(int k=1;k<=n;k++){ f[k][k]=1; for(int i=first[k];i;i=e[i].from)f[k][e[i].v]=-(1-Q)/d[e[i].v]; } gauss(); for(int i=1;i<=n;i++)printf("%.9lf\n",f[i][n+1]*Q); return 0; }
View Code

【BZOJ】1778: [Usaco2010 Hol]Dotp 驅逐豬玀