bzoj 3270: 博物馆

本文介绍了一个有趣的概率问题:两个朋友在一座特别设计的城堡博物馆中寻找彼此。通过计算每个房间中两人相遇的期望次数,利用高斯消元法求解线性方程组,最终得到在每个房间相遇的概率。

题意

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。这座博物馆有着特别的样式。它包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。
两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)
不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。
两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。
两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

题解

我们考虑一下这个概率怎么求考虑到,如果我们知道了
f[i][j]f[i][j]表示第一个人在i,第二个人在j的期望出现次数
因为,期望=可能的步数i*以这个步数走到这个点数的概率
同时两个人只可能见面一次
所以期望出现次数就是我们所要求的概率
然后高斯消元一波就可以了

CODE

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=25;
const long double eps=1e-10;
int n,m;
int a,b;
double d[N];
double p[N];
int P (int x,int y){return (x-1)*n+y;}
struct qq
{
    int x,y,last;
}e[N*N];int num,last[N];
void init (int x,int y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
double f[N*N][N*N];
void Gauss ()
{
    int cnt=n*n;
    for (int u=1;u<=cnt;u++)
    {
        if (f[u][u]<=eps)
        {
            for (int i=u;i<=cnt;i++)
            {
                if (f[i][u]>eps)
                {
                    for (int j=1;j<=cnt+1;j++)
                        swap(f[u][j],f[i][j]);
                    break;
                }
            }
        }
        for (int i=u+1;i<=cnt;i++)
        {
            double j=f[i][u]/f[u][u];
            for (int l=u;l<=cnt+1;l++)
                f[i][l]=f[u][l]*j-f[i][l];
        }
    }
    for (int u=cnt;u>=1;u--)
    {
        for (int i=u+1;i<=cnt;i++)
            f[u][cnt+1]=f[u][cnt+1]-f[u][i]*f[i][cnt+1];
        f[u][cnt+1]/=f[u][u];
    }
}
int main()
{
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for (int u=1;u<=m;u++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        d[x]=d[x]+1;d[y]=d[y]+1;
        init(x,y);init(y,x);
    }
    for (int u=1;u<=n;u++) scanf("%lf",&p[u]);
    for (int x1=1;x1<=n;x1++)//第一个人在哪个点 
        for (int x2=1;x2<=n;x2++)//第二个人在哪个点 
        {
            int o=P(x1,x2);
            //从o点出发
            f[o][o]=-1;
            if (x1!=x2) f[o][o]=f[o][o]+p[x1]*p[x2];
            for (int u=last[x1];u!=-1;u=e[u].last)
            {
                int y=e[u].y;
                //只有x1动
                if (y==x2) continue;
                f[o][P(y,x2)]=f[o][P(y,x2)]+p[x2]*(1-p[y])/d[y];
            }
            for (int u=last[x2];u!=-1;u=e[u].last)
            {
                int y=e[u].y;
                if (y==x1) continue;
                f[o][P(x1,y)]=f[o][P(x1,y)]+p[x1]*(1-p[y])/d[y];
            }
            for (int u=last[x1];u!=-1;u=e[u].last)
                for (int i=last[x2];i!=-1;i=e[i].last)
                {
                    int x=e[u].y,y=e[i].y;
                    if (x==y) continue;
                    f[o][P(x,y)]=f[o][P(x,y)]+(1-p[x])*(1-p[y])/d[x]/d[y];
                }
            /*for (int u=1;u<=n*n+1;u++) printf("%lf ",f[o][u]);
            printf("\n");*/
        }

    f[P(a,b)][n*n+1]=-1;
    /*for (int u=1;u<=n*n;u++)
    {
        for (int i=1;i<=n*n+1;i++)
            printf("%lf ",f[u][i]);
        printf("\n");
    }*/
    Gauss();
/*  for (int u=1;u<=n*n;u++)
        printf("%lf ",f[u][n*n+1]);*/
    for (int u=1;u<=n;u++) printf("%.6lf ",f[P(u,u)][n*n+1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值