[HDU4035]Maze 期望DP

说在前面

之前写了几道简单期望,感觉期望不过如此
然后就被HDU4035和HDU4089打脸了…….
看着别人的题解推公式都能推一个晚上,me好弱啊qwq


题目

…话说HDU原来的站挂掉了,现在的新站要在中间加一个split???
http://acm.split.hdu.edu.cn/
HDU4035传送门

题意

有一颗n个节点的树,n-1条边,根节点为1
在每个节点i都有概率发生以下事件:
1.被传送回1号节点—————————————概率为K[i]
2.找到出口并退出这棵树———————————概率为E[i]
3.沿着与i相连的边进入下一个点(可以返回父节点)
询问从1开始走到退出平均要走的步数

注:1号节点是没有出口的,1号节点也没有父节点


解法

(就对比着别人的推导自己又整理了一遍而已)
对于点i,我们设F[i]为从这个点开始,在退出时期望走出的边数

如果点i是叶子节点,那么有:

F[i]=F[1]K[i]+0E[i]+(1K[i]E[i])(F[fa]+1) F [ i ] = F [ 1 ] ∗ K [ i ] + 0 ∗ E [ i ] + ( 1 − K [ i ] − E [ i ] ) ∗ ( F [ f a ] + 1 )

(叶子节点的下一步可以是1号节点,也可以退出,也可以返回父节点)

如果点i不是叶子节点,那么有:

F[i]=F[1]K[i]+0E[i]+(1K[i]E[i]m(F[fa]+1+(F[child]+1))) F [ i ] = F [ 1 ] ∗ K [ i ] + 0 ∗ E [ i ] + ( 1 − K [ i ] − E [ i ] m ∗ ( F [ f a ] + 1 + ∑ ( F [ c h i l d ] + 1 ) ) )

(m代表的是i节点所连的边数,下同。非叶子节点的下一步,可以返回1号节点,也可以退出,也可以返回父节点或者走向任意一个子节点)

我们发现,两种F[i]都可以被表示成这样的形式

F[i]=F[1]A[i]+F[fa]B[i]+C[i] F [ i ] = F [ 1 ] ∗ A [ i ] + F [ f a ] ∗ B [ i ] + C [ i ]


设点对[i,j],其中i是j的父亲
那么有:

F[j]=F[1]A[j]+B[j]F[i]+C[j](F[j])=(A[j])F[1]+(B[j])F[i]+(C[j]) ⇒ F [ j ] = F [ 1 ] ∗ A [ j ] + B [ j ] ∗ F [ i ] + C [ j ] ⇒ ( ∑ F [ j ] ) = ( ∑ A [ j ] ) ∗ F [ 1 ] + ( ∑ B [ j ] ) ∗ F [ i ] + ( ∑ C [ j ] )

将以上式子带入对于i点的”非叶节点F[i]推导式”,得出:
F[i]=F[1]K[i]+(1K[i]E[i]m)(F[fa]+m+(A[j])F[1]+(B[j])F[i]+(C[j])) ⇒ F [ i ] = F [ 1 ] ∗ K [ i ] + ( 1 − K [ i ] − E [ i ] m ) ∗ ( F [ f a ] + m + ( ∑ A [ j ] ) ∗ F [ 1 ] + ( ∑ B [ j ] ) ∗ F [ i ] + ( ∑ C [ j ] ) )

通过移项,把F[i]移动到等式的左边,最终可以得到一个式子(我实在是不想写LaTeX了…手推一下吧qwq)


D[i]=11K[i]E[i]m(B[j]) D [ i ] = 1 − 1 − K [ i ] − E [ i ] m ∗ ( ∑ B [ j ] )

那么,对于F[i],他的A[i],B[i],C[i]就是:
A[i]=(K[i]+1K[i]E[i]m(A[j]))  / D[i]B[i]=(1K[i]E[i]m)  / D[i]C[i]=((1K[i]E[i])+1K[i]E[i]m(C[j]))  / D[i] A [ i ] = ( K [ i ] + 1 − K [ i ] − E [ i ] m ∗ ( ∑ A [ j ] ) )     /   D [ i ] B [ i ] = ( 1 − K [ i ] − E [ i ] m )     /   D [ i ] C [ i ] = ( ( 1 − K [ i ] − E [ i ] ) + 1 − K [ i ] − E [ i ] m ∗ ( ∑ C [ j ] ) )     /   D [ i ]

也就是说,对于每个点的A[i],B[i],C[i]值,我们都是可以由已知条件和子节点的A,B,C值求出来的,而叶节点的A,B,C值可以直接算(参见我的第一个公式”叶节点F[i]推导式”)
从下往上推,最终我们可以将F[1]用A,B,C表示出来。
那么最后答案就是

F[1]=A[1]F[1]+B[1]F[fa]+C[1]   (fa[1]==0) Ans=C[1]1A[1] ⇒ F [ 1 ] = A [ 1 ] ∗ F [ 1 ] + B [ 1 ] ∗ F [ f a ] + C [ 1 ]       ( f a [ 1 ] == 0 ) ⇒   A n s = C [ 1 ] 1 − A [ 1 ]


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

const double eps = 1e-9 ;//精度至少1e-9才能过
int T , N , tp , head[10005] , deg[10005] ;
double K[10005] , E[10005] , A[10005] , B[10005] , C[10005] ;
struct Path{
    int pre , to ;
}p[200005] ;

template <typename T>
int dcmp( T x ){
    if( x < 0 ) x = -x ;
    if( x <= eps && x >= -eps ) return 0 ;
    return x > 0 ;
}

void In( int t1 , int t2 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
}

void dfs( int u , int f ){
    double sumB = 0.0 ;
    A[u] = K[u] ; C[u] = ( 1 - K[u] - E[u] ) ; B[u] = C[u] / deg[u] ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v == f ) continue ;
        dfs( v , u ) ;
        A[u] += ( 1 - K[u] - E[u] ) / deg[u] * A[v] ;
        C[u] += ( 1 - K[u] - E[u] ) / deg[u] * C[v] ;
        sumB += ( 1 - K[u] - E[u] ) / deg[u] * B[v] ;
    }
    A[u] /= ( 1 - sumB ) ;
    C[u] /= ( 1 - sumB ) ;
    B[u] /= ( 1 - sumB ) ;
}


void clear(){
    memset( head , 0 , sizeof( head ) ) ;
    memset( deg , 0 , sizeof( deg ) ) ;
    memset( A , 0 , sizeof( A ) ) ;
    memset( B , 0 , sizeof( B ) ) ;
    memset( C , 0 , sizeof( C ) ) ;
    tp = 0 ;
}

int main(){
    scanf( "%d" , &T ) ;
    for( int te = 1 ; te <= T ; te ++ ){
        clear() ;
        scanf( "%d" , &N ) ;
        for( int i = 1 , u , v ; i < N ; i ++ ){
            scanf( "%d%d" , &u , &v ) ;
            In( u , v ) ; In( v , u ) ;
            deg[u] ++ ; deg[v] ++ ;
        }
        for( int i = 1 ; i <= N ; i ++ ){
            scanf( "%lf%lf" , &K[i] , &E[i] ) ;
            K[i] /= 100.0 ; E[i] /= 100.0 ;
        }
        dfs( 1 , -1 ) ;
        printf( "Case %d: " , te ) ;
        if( dcmp( 1 - A[1] ) == 0 ){
            puts( "impossible" ) ;
        }else printf( "%f\n" , C[1] / ( 1 - A[1] ) ) ;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值