hdu4035 Maze

链接

http://acm.hdu.edu.cn/showproblem.php?pid=4035

题解

就概率 dp d p
f[i] f [ i ] 表示从 i i 点出发走出迷宫的期望步数

fi=kif1+j1kieidifj

其中 j j 是枚举了和i相邻的点
那么显然我对于 n n 个节点可以列出n个方程,这 n n 个方程就能解出f1...fn f1 f 1 就是答案
高斯消元的复杂度是 O(n3) O ( n 3 ) ,不能用
这个结构比较奇特,它是树形结构,之前我做过一道环状转移的题:
https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81751128
环状直接设 f1 f 1 为变量,去递推其它的系数就行,但是树形不行,因为你会发现 fi f i 的式子里含有 fj f j fj f j 的式子里又含有 fi f i ,如果我想用 f1 f 1 表示 fi f i ,我得先计算 fj f j f1 f 1 怎么表示,这样就陷入了无尽的迭代
那就这样,我用 fj f j f1 f 1 表示 fi f i ,那么就能推出 fj f j 等于多少 f1 f 1
那现在考虑下顺序问题,显然我要 dfs d f s 遍历树,叶子节点和父亲组合成一组,我就能表示出父亲节点等于儿子的多少倍+父亲的多少倍+常数
这样一直向上回溯,相当于每次消掉当前点,最终得到只含有 f1 f 1 的式子,从而解出答案

代码

//树形概率DP 
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#define cl(x) memset(x,0,sizeof(x))
#define maxn 20100
#define eps 1e-8
#define inf (1e100)
using namespace std;
int N, tot, nex[maxn], head[maxn], to[maxn], d[maxn];
double A[maxn], B[maxn], C[maxn], k[maxn], e[maxn];
void adde(int a, int b){to[++tot]=b;nex[tot]=head[a];head[a]=tot;}
void init()
{
    cl(head), cl(to), cl(d), tot=0;
    int i, a, b;
    scanf("%d",&N);
    for(i=1;i<N;i++)scanf("%d%d",&a,&b), adde(a,b), adde(b,a), d[a]++, d[b]++;
    for(i=1;i<=N;i++)scanf("%lf%lf",k+i,e+i), k[i]/=100.0, e[i]/=100.0;
}
void dfs(int pos, int pre)
{
    int p;
    double sa=0, sb=0, sc=0, t;
    for(p=head[pos];p;p=nex[p])
        if(to[p]^pre)
        {
            dfs(to[p],pos);
            sa+=A[to[p]];
            sb+=B[to[p]];
            sc+=C[to[p]]+1;
        }
    if(pos!=1)
    {
        t=1-(1-k[pos]-e[pos])/d[pos]*sb;
        A[pos]=(k[pos]+(1-k[pos]-e[pos])/d[pos]*sa)/t;
        B[pos]=(1-e[pos]-k[pos])/d[pos]/t;
        C[pos]=((1-e[pos]-k[pos])/d[pos]*(sc+1))/t;
    }
    else
    {
        t=(1-(sa+sb)/d[1]);
        A[1]=0;
        C[1]=sc/d[1]/t;
    }
}
int main()
{
    int T, c;
    double ans;
    scanf("%d",&T);
    for(c=1;c<=T;c++)
    {
        init();
        dfs(1,0);
        printf("Case %d: ",c);
        ans=C[1];
        if(ans>inf)printf("impossible\n");
        else printf("%.10lf\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值