@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
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@
当作高斯消元复习的模板题好了。。。