POJ 4035 Maze(树形DP|期望)

题意:给一个树状图,从屋子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;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值