hdu 4035概率dp 求期望

仍然膜拜中。。。原文 仍是看了大神的blog才明白,看明白了以后就各种膜拜了。。。。

dp求期望的题。

题意:
有n个房间,由n-1条隧道连通起来,实际上就形成了一棵树,
从结点1出发,开始走,在每个结点i都有3种可能:
1.被杀死,回到结点1处(概率为ki)
2.找到出口,走出迷宫 (概率为ei)
3.和该点相连有m条边,随机走一条
求:走出迷宫所要走的边数的期望值。

设 E[i]表示在结点i处,要走出迷宫所要走的边数的期望。E[1]即为所求。

叶子结点:
E[i] = ki*E[1] + ei*0 + (1-ki-ei)*(E[father[i]] + 1);
= ki*E[1] + (1-ki-ei)*E[father[i]] + (1-ki-ei);

非叶子结点:(m为与结点相连的边数)
E[i] = ki*E[1] + ei*0 + (1-ki-ei)/m*( E[father[i]]+1 + ∑( E[child[i]]+1 ) );
= ki*E[1] + (1-ki-ei)/m*E[father[i]] + (1-ki-ei)/m*∑(E[child[i]]) + (1-ki-ei);

设对每个结点:E[i] = Ai*E[1] + Bi*E[father[i]] + Ci;

对于非叶子结点i,设j为i的孩子结点,则
∑(E[child[i]]) = ∑E[j]
= ∑(Aj*E[1] + Bj*E[father[j]] + Cj)
= ∑(Aj*E[1] + Bj*E[i] + Cj)
带入上面的式子得
(1 - (1-ki-ei)/m*∑Bj)*E[i] = (ki+(1-ki-ei)/m*∑Aj)*E[1] + (1-ki-ei)/m*E[father[i]] + (1-ki-ei) + (1-ki-ei)/m*∑Cj;
由此可得
Ai = (ki+(1-ki-ei)/m*∑Aj) / (1 - (1-ki-ei)/m*∑Bj);
Bi = (1-ki-ei)/m / (1 - (1-ki-ei)/m*∑Bj);
Ci = ( (1-ki-ei)+(1-ki-ei)/m*∑Cj ) / (1 - (1-ki-ei)/m*∑Bj);

对于叶子结点
Ai = ki;
Bi = 1 - ki - ei;
Ci = 1 - ki - ei;

从叶子结点开始,直到算出 A1,B1,C1;

E[1] = A1*E[1] + B1*0 + C1;
所以
E[1] = C1 / (1 - A1);

若 A1趋近于1则无解...



#include<stdio.h>
#include<string.h>
#include<cmath>
#include<algorithm>
using namespace std;
int cas,n,pos,m[20000],list[20000];
double ki[20000],ei[20000],ai[20000],bi[20000],ci[20000];
struct P{
	int u,v,next;
}point[20000];
void add(int fa,int son){
	point[pos].u=fa;
	point[pos].v=son;
	point[pos].next=list[fa];
	list[fa]=pos++;
}
bool dfs(int son,int fa){
	int now=list[son];
	if(m[son]==1&&fa!=-1){
		ai[son]=ki[son];
		ci[son]=bi[son]=1-ki[son]-ei[son];
		return true;
	}
	ai[son]=ki[son];
	bi[son]=(1-ki[son]-ei[son])/m[son];
	ci[son]=1-ki[son]-ei[son];
	now=list[son];
	double tmp=0;
	while(now!=-1){
		if(point[now].v==fa){
			now=point[now].next;
			continue;
		}
		if(dfs(point[now].v,son)==false){
			return false;
		}
		ai[son]+=ai[point[now].v]*bi[son];
		ci[son]+=ci[point[now].v]*bi[son];
		tmp+=bi[son]*bi[point[now].v];
		now=point[now].next;
	}
	if(fabs(tmp-1)<1e-10)return false;
	ai[son]/=1-tmp;
	bi[son]/=1-tmp;
	ci[son]/=1-tmp;
	return true;
}
int main(){
	scanf("%d",&cas);
	int it=1;
	while(cas--){
		memset(m,0,sizeof(m));
		pos=0;
		memset(list,-1,sizeof(list));
		scanf("%d",&n);
		for(int i=1;i<n;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y);add(y,x);
			m[x]++;m[y]++;
		}
		for(int i=1;i<=n;i++){
			scanf("%lf%lf",&ki[i],&ei[i]);
			ki[i]/=100.0;
			ei[i]/=100.0;
		}
		printf("Case %d: ",it++);
		if(dfs(1,-1)&&fabs(1-ai[1])>1e-10){
//			printf("! %.10lf\n",ai[1]);
			printf("%.6lf\n",ci[1]/(1-ai[1]));
		}
		else{
			printf("impossible\n");
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值