@bzoj - 3270@ 博物馆


@description@

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。
这座博物馆有着特别的样式。它包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。

两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。
等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)

不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。

两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。
更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。

两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

Input
第一行包含四个整数,n表示房间的个数;m表示走廊的数目;a,b (1 ≤ a, b ≤ n),表示两个男孩的初始位置。
之后m行每行包含两个整数,表示走廊所连接的两个房间。
之后n行每行一个至多精确到小数点后四位的实数 表示待在每间房间的概率。
题目保证每个房间都可以由其他任何房间通过走廊走到。

Output
输出一行包含n个由空格分隔的数字,注意最后一个数字后也有空格,第i个数字代表两个人在第i间房间碰面的概率(输出保留6位小数)
注意最后一个数字后面也有一个空格
Sample Input
2 1 1 2

1 2

0.5

0.5

扫描二维码关注公众号,回复: 6744456 查看本文章

Sample Output
0.500000 0.500000

HINT
对于100%的数据有 n <= 20,n-1 <= m <= n(n-1)/2

@solution@

很经典的高斯消元解无后效性 dp 模板,不过网上的某些题解并没有说清楚。。。
比如出现了我给某个概率 + 1 这种怎么推导都推导不出来的玄学东西。一个大于 1 的概率是几个意思啊。。。

我们尝试用期望代替概率进行 dp。注意题目中所说的如果两个人相遇,那么两个人将终止行动。
我们令 dp[(x, y)] 表示 “第一个人在 x,第二个人在 y” 这个事件的期望发生次数。
这样定义状态有什么好处呢?我们发现当两个人相遇后他们就不动了,于是 ∑dp[(i, i)] = 1,即“到达终点”这一事件期望发生次数为 1。
于是 dp[(i, i)] = P(i, i)*∑dp[(i, i)] = P(i, i),即两个人在 i 处相遇的期望发生次数 = 在 i 处相遇的概率 * 到达终点的期望发生次数 = 在 i 处相遇的概率。
于是我们通过求解期望就顺便把概率也求出来了。

至于 dp 的转移式,注意到一个状态其实有两个来源:一是它作为起点(即题目给定的 (a, b)),另一个是从上一个时刻的某一状态通过停留或移动得到。
分类讨论一下即可。然后就可以列出方程高消。

如果真的要用概率来推的话,我看到的一篇比较靠谱题解是这样推导:
令 b 向量表示初始状态下两个人在 (x, y) 的概率(显然只有 b[(a, b)] = 1)。
令 A 是一个转移矩阵,表示从前一时刻转移到现在这一时刻的概率转移,注意 (x, x) 是不能转移出去的。
则:ans = (I + A + A^2 + ... )*b = (I - A)^(-1)*b,因为每一个时刻两个人都有可能相遇。
写一个矩阵求逆即可。

@accepted code@

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int g[20 + 5][20 + 5], deg[20 + 5];
double M[20*20 + 5][20*20 + 5], P[20 + 5];
void gauss(int n, int m) {
    int r = 0, c = 0;
/*
    for(int i=0;i<n;i++) {
        for(int j=0;j<m;j++)
            printf("%lf ", M[i][j]);
        puts("");
    }
*/
    while( r < n && c < m ) {
        int mxr = r;
        for(int i=r;i<n;i++)
            if( fabs(M[i][c]) > fabs(M[mxr][c]) )
                mxr = i;
        for(int i=c;i<m;i++)
            swap(M[r][i], M[mxr][i]);
        if( M[r][c] ) {
            double k = M[r][c];
            for(int i=c;i<m;i++)
                M[r][i] /= k;
            for(int i=0;i<n;i++) {
                if( i == r || M[i][c] == 0 ) continue;
                k = M[i][c];
                for(int j=c;j<m;j++)
                    M[i][j] = M[i][j] - k*M[r][j];
            }
            r++;
        }
        c++;
    }
}
int n, m, a, b;
inline int id(int x, int y) {return x*n + y;}
int main() {
    scanf("%d%d%d%d", &n, &m, &a, &b), a--, b--;
    for(int i=1;i<=m;i++) {
        int x, y; scanf("%d%d", &x, &y), x--, y--;
        g[x][y] = g[y][x] = 1, deg[x]++, deg[y]++;
    }
    for(int i=0;i<n;i++)
        scanf("%lf", &P[i]);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++) {
            M[id(i, j)][id(i, j)] = -1;
            for(int x=0;x<n;x++)
                for(int y=0;y<n;y++) {
                    if( x == y ) continue;
                    if( i == x && j == y )
                        M[id(i, j)][id(x, y)] += P[x]*P[y];
                    if( i == x && j != y && g[j][y] )
                        M[id(i, j)][id(x, y)] += P[x]*(1 - P[y])/deg[y];
                    if( i != x && j == y && g[i][x] )
                        M[id(i, j)][id(x, y)] += (1 - P[x])/deg[x]*P[y];
                    if( i != x && j != y && g[i][x] && g[j][y] )
                        M[id(i, j)][id(x, y)] += (1 - P[x])/deg[x]*(1 - P[y])/deg[y];
                }
        }
    int p = n*n;
    M[id(a, b)][p] = -1;
    gauss(p, p + 1);
    for(int i=0;i<n;i++)
        printf("%lf ", M[id(i, i)][p]);
}

@details@

当作高斯消元复习的模板题好了。。。

猜你喜欢

转载自www.cnblogs.com/Tiw-Air-OAO/p/11136492.html