[BZOJ]3270: 博物馆 期望+高斯消元

Description

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

题解:

还是这个套路, f[i][j] 表示一个人在i,另外一个在j的期望次数,为什么是期望次数呢?首先因为这样好推,然后因为两人在一个房间实际上只有一次,所以概率其实是等于期望次数的,然后分四种情况讨论:1、两个人都不动。2、两个人都动。3、a动b不动。4、a不动b动。注意在任何时候都不能从一个i,j相等的状态转移到当前的状态。然后我犯了个错误:理所当然的认为 f[a][b]=1+ab ,结果错的一塌糊涂。其实就常规地推就好了,然后一开始的位置要+1。吐槽一下,一开始点开discuss,看错了,故意末尾没有空格,结果它居然有空格才能过?还没见过这样卡输出格式的。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=405;
const long double eps=1e-10;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,p1,p2;
struct Edge{int x,y,next;}e[Maxn*Maxn];
int last[Maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].x=x;e[t].y=y;e[t].next=last[x];last[x]=t;
}
long double a[Maxn][Maxn],p[Maxn];
void gauss()
{
    for(int i=1;i<=n*n;i++)
    {
        if(abs(a[i][i])<=eps)
        {
            for(int j=i+1;j<=n*n;j++)
            if(abs(a[j][i])>eps)
            {
                for(int k=i;k<=n*n+1;k++)swap(a[j][k],a[i][k]);
                break;
            }
        }
        for(int j=i+1;j<=n*n;j++)
        if(abs(a[j][i])>eps)
        {
            long double t=a[j][i]/a[i][i];
            for(int k=i;k<=n*n+1;k++)a[j][k]-=t*a[i][k];
        }
    }
    for(int i=n*n;i;i--)
    {
        for(int j=i+1;j<=n*n;j++)
        a[i][n*n+1]-=a[i][j]*a[j][n*n+1];
        a[i][n*n+1]/=a[i][i];
    }
}
int degree[Maxn],num[Maxn][Maxn],cnt=0;
int main()
{
    memset(a,0,sizeof(a));
    n=read();m=read();p1=read();p2=read();
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    num[i][j]=++cnt;
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        ins(x,y);ins(y,x);degree[x]++;degree[y]++;
    }
    for(int i=1;i<=n;i++)scanf("%Lf",&p[i]);
    for(int x1=1;x1<=n;x1++)
    for(int x2=1;x2<=n;x2++)
    {
        int o1=num[x1][x2];
        a[o1][o1]=-1.0;
        if(x1!=x2)a[o1][o1]+=p[x1]*p[x2];//两个都停在原地
        for(int i=last[x1];i;i=e[i].next)
        {int y1=e[i].y,o3=num[y1][x2];if(y1==x2)continue;a[o1][o3]+=(1.0-p[y1])*p[x2]/(long double)(degree[y1]);}
        for(int j=last[x2];j;j=e[j].next)
        {int y2=e[j].y,o2=num[x1][y2];if(x1==y2)continue;a[o1][o2]+=(1.0-p[y2])*p[x1]/(long double)(degree[y2]);}
        for(int i=last[x1];i;i=e[i].next)
        for(int j=last[x2];j;j=e[j].next)
        {
            int y1=e[i].y,y2=e[j].y,o4=num[y1][y2];
            if(y1==y2)continue;
            a[o1][o4]+=(1.0-p[y2])*(1.0-p[y1])/(long double)(degree[y1])/(long double)(degree[y2]);
        }
    }
    a[num[p1][p2]][n*n+1]=-1.0;
    gauss();
    for(int i=1;i<=n;i++)printf("%.6Lf ",a[num[i][i]][n*n+1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值