HDU4035 Maze(树上期望)

本文介绍了一种解决迷宫问题的高效算法,通过树形DP技术优化了从任意节点到达终点的期望步数计算,实现了O(n)的时间复杂度。

Maze

状态表示: f u f_u fu表示在 u u u节点走出迷宫期望次数, d u d_u du表示度数
首先很容易想到下面式子 f u = k u × f 1 + ( 1 − k u − e u ) × ∑ u → v f v + 1 d u f_u=k_u×f_1+(1-k_u-e_u)×\frac{\sum _{u\to v}f_v+1}{d_u} fu=ku×f1+(1kueu)×duuvfv+1
每个节点有一个类似的式子,也就是 n n n个式子, n n n个未知量我们需要求出 f 1 f_1 f1显然可以高斯消元 O ( n 3 ) O(n^3) O(n3)但是看了 1 ≤ n ≤ 1 0 4 1\leq n\leq 10^4 1n104留下了可惜的泪水~~

之前也做过返回起点的题目,只需要在dp时候多记录一维信息于是向初中解方程那样求解即可这样时间复杂度就能优化到 O ( n ) O(n) O(n),不过本题在树上,好像也不可行其实可以


对于“返回”在树上是如何体现的?

不难想到返回祖先,而此题中就是返回父亲以及返回根节点 1 1 1,于是效仿返回题目的做法,列出下面方程
f u = k u × f 1 + 1 − k u − e u d u × ( f f a + 1 ) + 1 − k u − e u d u × ∑ v ∈ s o n u ( f v + 1 ) f_u=k_u×f_1+\frac{1-k_u-e_u}{d_u}×(f_{fa}+1)+\frac{1-k_u-e_u}{d_u}×\sum_{v\in son_u}{(f_v+1)} fu=ku×f1+du1kueu×(ffa+1)+du1kueu×vsonu(fv+1)
稍微化简一下
f u = k u × f 1 + 1 − k u − e u d u × f f a + 1 − k u − e u d u × ∑ v ∈ s o n u f v + ( 1 − k u − e u ) f_u=k_u×f_1+\frac{1-k_u-e_u}{d_u}×f_{fa}+\frac{1-k_u-e_u}{d_u}×\sum_{v\in son_u}{f_v}+(1-k_u-e_u) fu=ku×f1+du1kueu×ffa+du1kueu×vsonufv+(1kueu)

我们发现所有dp式子都可以写成
f u = a u × f 1 + b u × f f a + c u f_u=a_u×f_1+b_u×f_{fa}+c_u fu=au×f1+bu×ffa+cu

由于 f a v = u fa_v=u fav=u上述式子还能化简得 f v = a v × f 1 + b v × f u + c v f_v=a_v×f_1+b_v×f_u+c_v fv=av×f1+bv×fu+cv
f u = k u × f 1 + 1 − k u − e u d u × f f a + 1 − k u − e u d u × ∑ v ∈ s o n u ( a v × f 1 + b v × f u + c v ) + ( 1 − k u − e u ) f_u=k_u×f_1+\frac{1-k_u-e_u}{d_u}×f_{fa}+\frac{1-k_u-e_u}{d_u}×\sum_{v\in son_u}{(a_v×f_1+b_v×f_u+c_v)}+(1-k_u-e_u) fu=ku×f1+du1kueu×ffa+du1kueu×vsonu(av×f1+bv×fu+cv)+(1kueu)
于是有更平凡的式子
( 1 − 1 − k u − e u d u ∑ v ∈ s o n u b v ) × f u = ( k u + 1 − k u − e u d u ∑ v ∈ s o n u a v ) × f 1 + 1 − k u − e u d u × f f a + 1 − k u − e u d u ∑ v ∈ s o n u c v + ( 1 − k u − e u ) (1-\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{b_v})×f_u=(k_u+\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{a_v})×f_1+\frac{1-k_u-e_u}{d_u}×f_{fa}+\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{c_v}+(1-k_u-e_u) (1du1kueuvsonubv)×fu=(ku+du1kueuvsonuav)×f1+du1kueu×ffa+du1kueuvsonucv+(1kueu)
于是有 a u = k u + 1 − k u − e u d u ∑ v ∈ s o n u a v 1 − 1 − k u − e u d u ∑ v ∈ s o n u b v a_u=\frac{k_u+\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{a_v}}{1-\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{b_v}} au=1du1kueuvsonubvku+du1kueuvsonuav
b u = 1 − k u − e u d u 1 − 1 − k u − e u d u ∑ v ∈ s o n u b v b_u=\frac{\frac{1-k_u-e_u}{d_u}}{1-\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{b_v}} bu=1du1kueuvsonubvdu1kueu
c u = 1 − k u − e u d u ∑ v ∈ s o n u c v + ( 1 − k u − e u ) 1 − 1 − k u − e u d u ∑ v ∈ s o n u b v c_u=\frac{\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{c_v}+(1-k_u-e_u)}{1-\frac{1-k_u-e_u}{d_u}\sum_{v\in son_u}{b_v}} cu=1du1kueuvsonubvdu1kueuvsonucv+(1kueu)

然后树形dp O ( n ) O(n) O(n)递推即可

#include<cstdio>
#include<cstring>
using namespace std;
constexpr int N=10010;
constexpr double eps=1e-10;
int h[N],e[2*N],ne[2*N],idx;
void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
int d[N],n;
double K[N],E[N],T[N],A[N],B[N],C[N];
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}
bool dfs(int u,int fa)
{
    int m=d[u];
    A[u]=K[u];
    B[u]=T[u]/m;
    C[u]=T[u];
    
    double temp=0;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int v=e[i];
        if(v==fa) continue;
        if(!dfs(v,u)) return 0;
        A[u]+=T[u]/m*A[v];
        temp+=T[u]/m*B[v];
        C[u]+=T[u]/m*C[v];
    }
    if(!sgn(1.0-temp)) return 0;
    A[u]/=(1-temp);
	B[u]/=(1-temp);
	C[u]/=(1-temp);
    return 1;
}
int main()
{
    int TT;
    scanf("%d",&TT);
    for(int ca=1;ca<=TT;ca++)
    {
        scanf("%d",&n);
        memset(h,-1,sizeof(int)*(n+1));idx=0;
        memset(d,0,sizeof(int)*(n+1));
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
            d[u]++,d[v]++;
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&K[i],&E[i]);
            K[i]/=100;
            E[i]/=100;
            T[i]=1-K[i]-E[i];
        }
        if(dfs(1,0)&&sgn(1.0-A[1]))
            printf("Case %d: %.10f\n",ca,C[1]/(1-A[1]));
        else    
            printf("Case %d: impossible\n",ca);
    }
    return 0;
}

太秒了吧!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值