●BZOJ 3270 博物馆

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=3270
题解:

期望DP,高斯消元
本来是定义的关于概率的dp,
但是发现这样定义有很多解释不通的地方(比如概率怎么会大于1,是否初始位置的概率就为1……)
然后看了大佬博客,明白了定义关于期望的dp才是正确的(至少可以解释的得通上面的问题,233)

把两个人所在的位置看成一个2元组(i,j),这个2元组状态表示一个人在i号点,另一个人在j号点。
然后令dp[i,j]表示到达状态(i,j)的期望次数。
然后考虑转移:
对于一个点i,定义pi表示呆在原地的概率,cnti表示i点的出度,ti=(1-pi)/cnti表示前往一个与i点相连的点的概率。
那么$$dp[i,j]=pi*pj*dp[i,j]+sum_{x->i,y->j}tx*pj*dp[x,j]+pi*ty*dp[i,y]+tx,ty*dp[x,y]$$
特别的:
1.形如dp[i,i]的状态不作为转移来源,因为到了(i,i)状态就结束了。
2.dp[X,Y]((X,Y)为起始状态)的来源还要多加上一个数值1,因为一开始就期望直接到了这个状态1次。
然后发现这个dp转移存在环,所以高斯消元,解出所有的dp值。

那么现在来了一个问题,题目要求的是概率,但是我们现在求出的却是期望???
显然此时在每个结束状态碰面的概率就等于每个状态的期望到达次数/期望和。
同时因为每当到达一个结束状态就会停下来,
所以所有的结束状态的dp值之和,也就是期望之和等于1(期望到达所以结束状态的次数为1)
那么每个结束状态的期望值在数值上就等于概率值啦。


代码:

#include<bits/stdc++.h>
#define MAXN 25
using namespace std;
double a[MAXN*MAXN][MAXN*MAXN],ans[MAXN*MAXN],p[MAXN];
double *A[MAXN*MAXN];
bool Edge[MAXN][MAXN];
int N,M,Ant,X,Y;
int idx(int i,int j){return (i-1)*N+j;}
void Gausselimination(int pos,int i){
	if(pos==Ant+1||i==N*N+1) return;
	for(int j=pos;j<=Ant;j++) if(A[j][i]!=0){
		swap(A[pos],A[j]); break;
	}
	if(A[pos][i]!=0){
		for(int j=pos+1;j<=Ant;j++){
			double k=A[j][i]/A[pos][i];
			for(int l=i;l<=N*N+1;l++)
				A[j][l]-=A[pos][l]*k;
		}
	}
	Gausselimination(pos+(A[pos][i]!=0),i+1);
	if(A[pos][i]!=0){
		for(int l=i+1;l<=N*N;l++) ans[i]+=ans[l]*A[pos][l];
		ans[i]=A[pos][N*N+1]-ans[i];
		ans[i]/=A[pos][i];
	}
}
void buildequation(){
	double pi,pj,tx,ty;
	static int cnt[MAXN];
	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			if(Edge[i][j]) cnt[i]++;
	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++){
			Ant++; pi=p[i]; pj=p[j];
			a[Ant][idx(i,j)]=-1;
			if(i==X&&j==Y) a[Ant][N*N+1]=-1;
			if(i!=j) a[Ant][idx(i,j)]+=pi*pj;
			for(int x=1;x<=N;x++) if(Edge[x][i]){
				tx=(1-p[x])/cnt[x];
				for(int y=1;y<=N;y++) if(Edge[y][j]){
					ty=(1-p[y])/cnt[y];
					if(i!=y) a[Ant][idx(i,y)]=pi*ty;
					if(x!=j) a[Ant][idx(x,j)]=tx*pj;
					if(x!=y) a[Ant][idx(x,y)]=tx*ty;
				}
			}
		}
	for(int i=1;i<=Ant;i++) A[i]=a[i];
}
int main(){
	scanf("%d%d%d%d",&N,&M,&X,&Y);
	for(int i=1,u,v;i<=M;i++)
		scanf("%d%d",&u,&v),Edge[u][v]=Edge[v][u]=1;
	for(int i=1;i<=N;i++) scanf("%lf",&p[i]);
	buildequation();
	Gausselimination(1,1);
	for(int i=1;i<=N;i++) printf("%.6lf ",ans[idx(i,i)]);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8541949.html