• 【HDU4035】Maze-期望DP+树形DP


    测试地址:Maze

    题目大意:有一个树形的迷宫,有N个房间(标号为1~N)以及N-1条通道将它们连通,一开始在1号房间,每进入一个房间i,有k[i]的概率被陷阱杀死回到房间1,有e[i]的概率找到出口逃离迷宫,如果没有找到出口也没有被杀,那么就在与该房间相连的通道中等概率随机选一条走,求逃离迷宫所需要走的通道数的期望值(如果不能逃离输出impossible)。

    做法:求期望采用逆推的方式。设f[i]表示从房间i开始走,逃离迷宫所需要走的通道数的期望值,令fa[i]为点i的父亲(整棵树以1为根时),deg[i]为点i的度数,我们知道对于每个叶子节点有:f[i]=k[i]*f[1]+(1-k[i]-e[i])*(f[fa[i]]+1),对于每个节点有:f[i]=k[i]*f[1]+(1-k[i]-e[i])/deg[i]*Σ(f[j]+1),其中j满足点i和点j之间有直接连边。我们可以发现,一个节点的期望值跟三个部分有关:点1的期望,父亲节点的期望,儿子节点的期望,而儿子节点的期望也和这三个部分有关,一直下到叶子节点,因为叶子节点没有儿子节点,所以我们可以把叶子节点作为突破口。我们可以将每个点的期望表示成这样一个式子:f[i]=x*f[1]+y*f[fa[i]]+z,显然对于叶子节点有:x=k[i],y=1-k[i]-e[i],z=1-k[i]-e[i]。然后对于其他节点,因为其儿子节点的父亲节点就是它本身,所以我们可以用待定系数法求出这个节点的期望式。在最后,我们可以求出f[1]的期望式,即f[1]=x*f[1]+y*f[fa[1]]+z,由于fa[1]没有意义,我们把它舍去,变化一下式子就可以得到:f[1]=z/(1-x),这就是我们要求的答案了。注意当x无限趋近于1时(|x-1|≤1e-9),f[1]无法解出,这时就输出impossible就可以了。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #define eps 1e-9
    using namespace std;
    int T,n,first[10010],tot,fa[10010];
    double f1[10010],f2[10010],f3[10010],k[10010],e[10010]; //f1,f2,f3即上文提到的系数x,y,z
    struct edge {int v,next;} E[20010];
    bool flag;
    
    void insert(int a,int b)
    {
      E[++tot].v=b,E[tot].next=first[a],first[a]=tot;
    }
    
    void treedp(int v)
    {
      double s=1,a;
      f1[v]=f2[v]=f3[v]=0;
      for(int i=first[v];i;i=E[i].next)
        if (E[i].v!=fa[v])
    	{
    	  fa[E[i].v]=v;
    	  treedp(E[i].v);
    	  f1[v]+=f1[E[i].v];
    	  f2[v]+=f2[E[i].v];
    	  f3[v]+=f3[E[i].v];
    	  s+=1.0;
    	}
      if (v==1) s-=1.0;
      f1[v]=f1[v]*(1-k[v]-e[v])/s;
      f2[v]=f2[v]*(1-k[v]-e[v])/s;
      f3[v]=f3[v]*(1-k[v]-e[v])/s;
      a=1-f2[v];
      f1[v]+=k[v];
      f2[v]=(1-k[v]-e[v])/s;
      f3[v]+=(1-k[v]-e[v]);
      f1[v]/=a,f2[v]/=a,f3[v]/=a;
    }
    
    int main()
    {
      scanf("%d",&T);
      for(int t=1;t<=T;t++)
      {
        scanf("%d",&n);
    	memset(first,0,sizeof(first));
    	tot=0;
    	for(int i=1,a,b;i<n;i++)
    	{
    	  scanf("%d%d",&a,&b);
    	  insert(a,b),insert(b,a);
    	}
    	for(int i=1;i<=n;i++)
    	{
    	  scanf("%lf%lf",&k[i],&e[i]);
    	  k[i]/=100.0,e[i]/=100.0;
    	}
    	
    	fa[1]=0;
    	treedp(1);
    	
    	if (fabs(f1[1]-1)<=eps) printf("Case %d: impossible
    ",t);
    	else printf("Case %d: %.6lf
    ",t,f3[1]/(1-f1[1]));
      }
      
      return 0;
    }
    


  • 相关阅读:
    github上比较有名的一个前端面试题,随便做做(4)
    常见动态内存的管理程序错误
    .Net笔记(一)is和 as
    消息映射宏
    组态软件分析(第一节)
    指针形参与引用形参区别
    MFC 消息映射的产生
    WPF 路由事件(一)
    C# 封装集合
    Xaml Code Behind Generator (XAML 转成C#代码)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793763.html
Copyright © 2020-2023  润新知