bzoj3270-博物馆

题目

给出一个无向图,两个人初始在两个点上。当一个人在一个点(i)上的时候,每一次,他有(p[i])的概率留在原位,有(1-p[i])的概率等概率地选择直接连边的一个点走出去。当两个人在同一时刻走到同一个点,那么他们相遇,过程结束。现在求他们在每一个点相遇的概率。

分析

看似这是一个简单的概率dp,其实不然。发现图中如果出现环,那么dp的顺序无法保证。hjh说这和一个复杂电路中的电阻差不多。这题中概率无法直接dp,因为概率的性质需要容斥什么的,很麻烦,所以我们把它转化为简单的期望求解。
(f[x][y])表示两人一个在(x)一个在(y)这种状态的期望发生次数,那么就有可以通过四种情况的讨论列出方程:

  • 从i走到x,y不动:(f[i][y]*(1-p[i])/z[i]*p[y])
  • 从j走到y,x不动:(f[x][j]*(1-p[j])/z[j]*p[x])
  • 从i走到x,从j走到y:(f[i][j]*(1-p[i])*(1-p[j])/z[i]/z[j])
  • 留在原地:(f[x][y]*p[x]*p[y])
    这里一定要注意,(i e j),因为相遇后不会继续走。
    然后就开始列方程,最后一个问题是初始状态怎么处理。
    我们计算的是期望经过的次数,而起点最开始就经过了,所以其实起点的(f[x][y])等于上述四种情况再加上原有的1,所以移项到右边变成-1.
    由于是矩阵,所以我们要把(f[x][y])编个号,比如((n-1)*x+y)之类的,能用即可。
    消元之后,我们就可以得到每个(f[x][y])的值。由于到达终点的总期望是1次,所以这里的概率就等于期望,直接输出答案即可。

代码

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=21;
const int maxm=maxn*maxn;
typedef vector<int> vec;
typedef vec::iterator itt;
vec a[maxn];
int z[maxn],n,all;
double p[maxn];
double g[maxm][maxm];
void sw(double a[],double b[]) {
	for (int i=1;i<=all+1;++i) swap(a[i],b[i]);
}
void elm() {
	for (int i=1;i<all;++i) {
		if (g[i][i]==0) {
			for (int j=i+1;j<=all;++j) if (g[j][i]) {
				sw(g[i],g[j]);
				break;
			}
		}
		for (int j=i+1;j<=all;++j) {
			double tmp=g[j][i]/g[i][i];
			for (int k=1;k<=all+1;++k) {
				g[j][k]-=g[i][k]*tmp;
			}
		}
	}
	for (int i=all;i>1;--i) {
		if (g[i][i]==0) {
			for (int j=i-1;j>0;--j) if (g[j][i]) {
				sw(g[i],g[j]);
				break;
			}	
		}	
		for (int j=i-1;j>0;--j) {
			double tmp=g[j][i]/g[i][i];
			for (int k=1;k<=all+1;++k) {
				g[j][k]-=g[i][k]*tmp;
			}
		}
	}
}
int get(int x,int y) {
	return (x-1)*n+y;
}
int main() {
	#ifndef ONLINE_JUDGE
		freopen("test.in","r",stdin);
	#endif
	int m,s,t;
	scanf("%d%d%d%d",&n,&m,&s,&t),all=n*n;
	for (int i=1;i<=n;++i) a[i].push_back(i);
	for (int i=1;i<=m;++i) {
		int u,v;
		scanf("%d%d",&u,&v);
		a[u].push_back(v),a[v].push_back(u);
		z[u]++,z[v]++;
	}
	for (int i=1;i<=n;++i) scanf("%lf",p+i);
	for (int x=1;x<=n;++x) for (int y=1;y<=n;++y) {
		int idn=get(x,y);
		g[idn][idn]=-1;
		for (itt it=a[x].begin();it!=a[x].end();++it) {
			for (itt jt=a[y].begin();jt!=a[y].end();++jt) {
				int i=*it,j=*jt,id=get(i,j);
				if (i==j) continue;
				if (i==x && j==y) g[idn][id]+=p[x]*p[y]; else 
				if (i==x) g[idn][id]+=p[x]*(1-p[j])/z[j]; else 
				if (j==y) g[idn][id]+=p[y]*(1-p[i])/z[i]; else 
				g[idn][id]+=(1-p[i])*(1-p[j])/z[i]/z[j];
			}	
		}
	}
	g[(s-1)*n+t][all+1]=-1;
	elm();
	for (int i=1;i<=n;++i) printf("%.6lf ",g[(i-1)*n+i][all+1]/g[(i-1)*n+i][(i-1)*n+i]);
	puts("");
}
原文地址:https://www.cnblogs.com/owenyu/p/6724719.html