【BZOJ】1778: [Usaco2010 Hol]Dotp 驱逐猪猡

【题意】给定无向图,炸弹开始在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 ightarrow 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<cmath>
#include<algorithm>
using namespace std;
const int maxn=310;
long double a[maxn][maxn];
int n,m,pp,qq,out[maxn];
void gauss(){
    for(int i=1;i<n;i++){
        int r=i;
        for(int j=i+1;j<=n;j++)if(fabs(a[j][i])>fabs(a[r][i]))r=j;//////
        if(r!=i)for(int j=i;j<=n+1;j++)swap(a[r][j],a[i][j]);
        for(int j=i+1;j<=n;j++){
            for(int k=n+1;k>=i;k--){
                a[j][k]-=a[j][i]/a[i][i]*a[i][k];
            }
        }
    }
    for(int i=n;i>=1;i--){
        for(int j=i+1;j<=n;j++)a[i][n+1]-=a[i][j]*a[j][n+1];
        a[i][n+1]/=a[i][i];
    }
}
int main(){
    
    int pp,qq;
    long double Q;
    scanf("%d%d%d%d",&n,&m,&pp,&qq);
    Q=1.0*pp/qq;
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        a[v][u]+=(1-Q);
        if(u!=v)a[u][v]+=(1-Q);
        out[u]++;out[v]++;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)if(out[j])a[i][j]=a[i][j]/out[j];
        a[i][i]--;
    }
    a[1][n+1]=-1;
    gauss();
    for(int i=1;i<=n;i++)printf("%.9Lf
",a[i][n+1]*Q+(1e-13));///////////////
    return 0;
}
View Code

注意:

1.高斯消元过程中每次要找绝对值最大的主元,这是为了避免除零,提高精度。

2.涉及负数的浮点数最后要避免-0,加eps

原文地址:https://www.cnblogs.com/onioncyc/p/7613829.html