zoukankan      html  css  js  c++  java
  • HDU4035 Maze 期望DP+树形DP(好题)

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

    解法:这道题非常锻炼处理式子的能力,非常值得一做!讲一下推理过程:

    首先容易想到状态设计:dp[i]代表从i点出发直到离开迷宫的期望步数,那么写出状态转移方程:

    dp[x]=k[x]*dp[1] + (1-k[x]-s[x]) * (dp[fa]+1 sigma(dp[son]+1) ) /du[x] ;

    观察这个式子,dp[x]与4个部分有关:dp[1]  ,dp[fa] , dp[son] , 常数项C

    令人沮丧的是dp与fa和son都有关系意味着有环,那么就得使用高斯消元解决。但是注意到数据量10000高斯消元显然超时。我们只能另寻他法。

    我们要尽量利用树的特性,儿子与父亲有密切关系。注意到叶子结点的dp方程是没有儿子项的,那么能不能利用这个把环消掉?如果能利用叶子一层层往上代的话也许能消去全部的儿子项!!

    这样的话我们尝试把所有的方程写成  dp[i]=Ai*dp[1] + Bi*dp[fa] + Ci   (没有儿子项)

    这样的话  叶子dp[son]=k[son]*dp[1] + (1-k[x]-s[x])*(dp[fa]+1) 的 Ai=k[i]  Bi=1-k[i]-s[i]  Ci=1-k[i]-s[i]

    我们把叶子写出 dp[son]=Ason*dp[1] + Bson*dp[fa] + Cson  带入到他的父亲方程(注意此时dp[son]=Ason*dp[1] + Bson*dp[x] + Cson  即fa变成了x)中得到

    du[x]dp[x] - (1-k[i]-s[i])(sigmaBson)dp[x] = (du[x]*k[x]+(1-k[x]-s[x])(sigmaAson)) *dp[1]  +  (1-k[x]-s[x])*dp[fa]  +  (1-k[x]-s[x])*du[x] + (1-k[x]-s[x])*(sigmaCson)

    虽然这个式子很长,但是可以看得出 Ax  Bx  Cx

    那么到这里也就意味着我们能从叶子一直往上推我们就能得到每个点的Ax Bx Cx。那么一直到根节点1的时候就能得到答案,dp[1]=C1/(1-A1)。

    要注意也就是A1=1的时候无解。且这题卡精度了eps要1e-9。

    代码如下。但是建议大家自己完完整整推一次式子然后自己写程序完成。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e4+10;
    const double eps=1e-9;
    int n,m;
    double k[N],s[N];
    vector<int> G[N];
    
    double A[N],B[N],C[N];
    void dfs(int x,int fa) {
        if (G[x].size()==1 && x!=1) {
            A[x]=k[x]; B[x]=1-k[x]-s[x]; C[x]=1-k[x]-s[x];
            return;
        }
        double du=G[x].size(),go=1-k[x]-s[x];
        A[x]=du*k[x]; B[x]=go; C[x]=go*du;
        double t=G[x].size();
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (y==fa) continue;
            dfs(y,x);
            A[x]+=go*A[y];
            C[x]+=go*C[y];
            t-=go*B[y];
        }
        A[x]/=t; B[x]/=t; C[x]/=t;
    }
    
    int main()
    {
        int T,cas=0; cin>>T;
        while (T--) {
            scanf("%d",&n);
            for (int i=1;i<=n;i++) G[i].clear();
            for (int i=1;i<n;i++) {
                int x,y; scanf("%d%d",&x,&y);
                G[x].push_back(y);
                G[y].push_back(x);
            }
            for (int i=1;i<=n;i++) scanf("%lf%lf",&k[i],&s[i]);
            for (int i=1;i<=n;i++) k[i]/=100.0,s[i]/=100.0;
            
            memset(A,0,sizeof(A));
            memset(B,0,sizeof(B));
            memset(C,0,sizeof(C));
            dfs(1,0);
            if (fabs(A[1]-1.0)<eps) printf("Case %d: impossible
    ",++cas);
            else printf("Case %d: %.6lf
    ",++cas,C[1]/(1-A[1]));
        }
        return 0;
    }
  • 相关阅读:
    Maven中配置生成单元测试报告配置
    maven-surefire-plugin总结
    Java程序内存分析:使用mat工具分析内存占用
    利用内存分析工具(Memory Analyzer Tool,MAT)分析java项目内存泄露
    JVM性能监控工具(一)-jdk命令行工具
    Java线程详细监控和其dump的分析使用—-分析Java性能瓶颈
    JDK自带内存及线程分析工具
    使用jstack和TDA进行java线程dump分析
    qt 插件开发
    精通linux设备驱动开发 笔记
  • 原文地址:https://www.cnblogs.com/clno1/p/11637409.html
Copyright © 2011-2022 走看看