题意:给一个树状图,从屋子1开始,等概率选择一条路走过去,对于屋子 i 会有概率 ki 被杀死(被传送到屋子1),ei逃脱(结束),问逃脱的期望是多少。
思路:用 dp [ i ] 表示在屋子 k 逃脱的期望步数,那么有以下可能:
1.被杀死,概率为k [ i ]那么被转移到dp[ 1 ] ,注意此时不产生步数的消耗(刚开始没考虑结果没过样例)
2.跑掉,那么就终止了,概率e [ i ] ,,转移量 0 。
3.(非叶子节点)选择某一个孩子节点,每一个孩子节点的概率为 ( 1 - ki - ei ) / mi ( mi为 i 节点的度 ),转移量为dp[ son ] + 1
4.跑到父节点,概率( 1 - ki - ei ) / mi 转移为dp[ father ] + 1
然后算概率, 就会发现又有前后顺序的干扰,讨论:
叶子节点:
dp[ i ] = ki * dp[ 1 ] + ( 1 - ki - ei ) * ( dp [ father ] + 1 )
非叶子节点:
dp [ i ] = ki* dp[ 1 ] + ( 1 - ki - ei ) / mi * ( dp[ father ] + 1 ) +( 1 - ki - ei ) / mi * sgm( dp [ son ] + 1 )
整理可得:
叶子节点:
dp [ i ] = ki * dp[ 1 ] + ( 1 - ki - ei ) * dp [ father ] + 1 - ki - ei
非叶子节点:
dp [ i ] = ki* dp[ 1 ] + ( 1 - ki - ei ) / mi * dp[ father ] + ( 1 - ki - ei ) / mi * sgm( dp [ son ] + 1 ) +1 - ki - ei
构造一个关系式:
dp [ i ] = A[ i ] dp[ 1 ] + B [ i ] dp [ father ] + C[ i ]
显然如果是叶子节点 A[ i ] = ki , B[ i ] = 1 - ki - ei ,C[ i ] = 1 - ki - ei
如果是非叶子,就将子节点的表达式迭代入父节点,可以得到A,B,C的关系,那么从叶子节点向上迭代,可以拿到A1,B1,C1的值,然后dp[ 1 ] 就可以求解了,注意可能存在出不来的情况,那么这个期望就应该是正无穷,而对应的作为分母的值就应该近似为0,每次都判断下(不一定是从1) ,然后就OK。。。。纪念我第一个树形DP概率题以及一个上午的时间。。。。。
#include<cstdio>
#include<vector>
using namespace std;
#define N 10010
#define eps 1e-9
#define pb push_back
double e[N];
double k[N];
double r[N];
double a[N];
double b[N];
double c[N];
vector<int> v[N];
double abs(double k){
if(k>0) return k;
return -k;
}
bool dfs(int sta,int pre){
int i,j;
int m=v[sta].size();
a[sta]=k[sta];
b[sta]=r[sta]/m;
c[sta]=r[sta];
double sumA,sumB,sumC;
sumA=sumB=sumC=0;
for(i=0;i<v[sta].size();i++){
int now=v[sta][i];
if(now==pre) continue;
if(!dfs(now,sta)) return false;
sumA+=a[now];
sumB+=b[now];
sumC+=c[now];
}
double under=1-r[sta]/m*sumB;
if(abs(under)<eps) return false;
a[sta]+=r[sta]/m*sumA;
a[sta]/=under;
b[sta]/=under;
c[sta]+=r[sta]/m*sumC;
c[sta]/=under;
return true;
}
int main(){
int i,j,t,cas,n;
while(scanf("%d",&t)!=EOF){
for(cas=1;cas<=t;cas++){
scanf("%d",&n);
int from,to;
for(i=0;i<=n;i++){
v[i].clear();
}
for(i=1;i<n;i++){
scanf("%d %d",&from,&to);
v[from].pb(to);
v[to].pb(from);
}
for(i=1;i<=n;i++){
scanf("%lf %lf",&k[i],&e[i]);
k[i]/=100.0;
e[i]/=100.0;
r[i]=1-k[i]-e[i];
}
printf("Case %d: ",cas);
if(!dfs(1,-1)||abs(1-a[1])<eps) printf("impossible\n");
else printf("%.6f\n",c[1]/(1-a[1]));
}
}
return 0;
}