【题意】给定无向图,炸弹开始在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; }