[BZOJ 3270]博物馆

题意

有两个人在无向图中走,初始时在 (A,B) p[i] 的概率留在原地, (1p[i]) 的概率等可能的往出边走,询问两个人在1~n每个房间相遇的概率
n20

分析

考虑两个人会构成 n2 种状态 (a,b)
则可以列出方程,设 P(a,b) 为经过 (a,b) 这个状态的概率
p[i] 代表留在当前点的概率, Out[i] 代表走出当前点的概率
Out[i]=(1p[i])/degree[i]
P(a,b)=p[a]p[b]P(a,b)+Out[u]p[b]P(u,b)+p[a]Out[v]P(a,v)+Out[u]Out[v]P(u,v)
- 注意到如果两人在同一个房间的话就不可以再走了,所以当u==v的时候系数为0
然后在状态 (A,B) 起始点的时候方程为
P(a,b)=p[a]p[b]P(a,b)+Out[u]p[b]P(u,b)+p[a]Out[v]P(a,v)+Out[u]Out[v]P(u,v)+1
高斯消元即可

#include <bits/stdc++.h>
#define maxn 10010
#define st(i, j) (i-1)*n+j
using namespace std;

int n, m;

int deg[maxn];

struct Edge{int to, next;}edge[maxn];
int h[maxn], cnt, s, t;
void add(int u, int v){
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].next = h[u];
    h[u] = cnt;
    deg[u] ++;
}

double p[maxn], Out[maxn];

double a[510][510];

void Debug(int n){
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= n + 1; j ++){
            printf("%.2lf ", a[i][j]);
        }printf("\n");
    }puts("");
}


void build(int x, int y){
    int now = st(x, y);
    a[now][now] -= 1;

    for(int i = h[x]; i; i = edge[i].next){
        for(int j = h[y]; j; j = edge[j].next){
            int u = edge[i].to, v = edge[j].to, to = st(u, v);
            if(u != v){
                if(u == x && v == y)a[now][to] += p[u] * p[v];
                else if(u == x)a[now][to] += p[u] * Out[v];
                else if(v == y)a[now][to] += Out[u] * p[v];
                else a[now][to] += Out[u] * Out[v];
            }
        }
    }
}

void Gauss(int n){
    for(int i = 1; i <= n; i ++){
        for(int j = i; j <= n; j ++)if(a[j][i]){
            for(int k = 1; k <= n + 1; k ++)
                swap(a[i][k], a[j][k]);
            for(int k = 1; k <= n + 1; k ++)
                if(k != i)a[i][k] /= a[i][i];
            a[i][i] = 1;
            break;
        }
        if(!a[i][i])continue;
        for(int j = 1; j <= n; j ++){
            if(j == i)continue;
            double t = a[j][i];
            for(int k = 1; k <= n + 1; k ++)
                a[j][k] -= t * a[i][k];
        }
    }
}

int main(){
    scanf("%d%d%d%d", &n, &m, &s, &t);
    int u, v;
    for(int i = 1; i <= m; i ++){
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
    }

    for(int i = 1; i <= n; i ++)
        scanf("%lf", &p[i]);
    for(int i = 1; i <= n; i ++)
        Out[i] = (1 - p[i]) / deg[i];
    for(int i = 1; i <= n; i ++) add(i, i);
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            build(i, j);

    a[st(s, t)][n * n + 1] = -1;

    Gauss(n * n);

    for(int i = 1; i <= n; i ++){
        int now = st(i, i);
        printf("%.6lf ", a[now][n * n + 1]);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值