hdu 4035 Maze (概率与期望DP)

本文介绍了一种使用树形动态规划解决特定问题的方法,即在一棵树上,从根节点出发,在每一步都有可能被杀死回到起点或者直接逃离的情况下,求解逃离的期望步数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

传送门

题目描述:一棵树,一个人初始在1号点。每次到达一个点,有ki的概率被杀死,并且回到1号点,有ei的概率直接逃离,然后等概率的逃到与他相邻的节点(1-ki-ei)/du[i],每次移动步数+1。 求逃出去的期望步数。

题解

这道题与上面题的思想和方法是类似的。
我们先假设逃离是到达节点0, 那么E[0]=0,倒着推。
对于叶子节点,他要么逃到他的父亲节点,要么被杀,要么直接逃离
E[i]=(1kiei)E[fa[i]]+kiE[1]+E[0]ei
因为E[0]=0 ,所以式子最终可以化简成 E[i]=(1kiei)E[fa[i]]+kiE[1]+(1kiei)
那么在这个式子中 E[fa[i]],E[1] 是未知量,那么我们可以把式子化简成
E[i]=A[i]E[fa[i]]+B[i]E[1]+C[i] 的形式。
对于非叶子节点
E[i]=(E[fa[i]]+1+(E[son[i]]+1))(1kiei)du[i]+E[1]ki
对于E[son[i]]来说,如果化成 E[i]=A[i]E[fa[i]]+B[i]E[1]+C[i] 的形式,那么我们可以发现 E[fa[son[i]]] 实际上就是E[i],那么我们可以对式子进行进一步的化简。
E[i]=(E[fa[i]]+1+(A[son[i]]E[i]+B[son[i]]E[1]+C[son[i]]+1))(1kiei)du[i]+E[1]ki
我们设 (1kiei)du[i]=p[i] 式子整理得
(1p[i]A[son[i]])E[i]=p[i]E[fa[i]]+(ki+p[i]B[son[i]])E[1]+(1kiei)+p[i]C[son[i]]
最后把 (1p[i]A[son[i]]) 除过来,就可以得到 A[i],B[i],C[i]
最后的答案就是 C[1]/(1B[1])
注意在计算的过程中只要E[i]的系数趋近与0,那就是impossible

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
#define eps 1e-9
using namespace std;
int n,point[N],v[N],nxt[N],du[N],tot,T;
double A[N],B[N],C[N],k[N],e[N];
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; 
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
bool dfs(int x,int fa)
{
    A[x]=(1-k[x]-e[x])/du[x];
    B[x]=k[x];
    C[x]=1-k[x]-e[x];
    double t=1.0; double h=A[x];
    for (int i=point[x];i;i=nxt[i]) {
        if (v[i]==fa) continue;
        if (!dfs(v[i],x)) return false;
        t-=h*A[v[i]];
        B[x]+=h*B[v[i]];
        C[x]+=h*C[v[i]];
    }
    if (fabs(t)<eps) return false;
    A[x]/=t; B[x]/=t; C[x]/=t;
//  cout<<x<<" "<<A[x]<<" "<<B[x]<<" "<<C[x]<<endl;
    return true;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d",&T);
    for (int t=1;t<=T;t++) {
        tot=0;  scanf("%d",&n);
        memset(point,0,sizeof(point));
        memset(du,0,sizeof(du));
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(C,0,sizeof(C));
        for (int i=1;i<n;i++) {
            int x,y; scanf("%d%d",&x,&y);
            add(x,y); du[x]++; du[y]++;
        }
        for (int i=1;i<=n;i++) {
            scanf("%lf%lf",&k[i],&e[i]);
            k[i]/=100.0; e[i]/=100.0;
        }
        bool pd=dfs(1,0);
        printf("Case %d: ",t);
        if (fabs(1.0-B[1])<eps||!pd) printf("impossible\n");
        else printf("%.6lf\n",C[1]/(1-B[1]));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值