[BZOJ3566][SHOI2014]概率充电器(概率DP)

本文深入探讨了树形概率动态规划问题,通过具体实例讲解如何计算每个节点在树状结构中能够获得电力的概率,采用逆向思维将问题转化为计算无法通电的概率,提供了一种有效的算法实现。

题意:树上每个点有概率有电,每条边有概率导电,求每个点能被通到电的概率。

较为套路但不好想的概率DP。

树形DP肯定先只考虑子树,自然的想法是f[i]表示i在只考虑i子树时,能有电的概率,但发现无法转移,因为只要有任何一个儿子同时满足“儿子有电且儿子到i的边导电”,这个点就能导电,而“或”命题在外层的概率通常因为容易算重而不好计算。

正难则反,考虑f[i]表示i在只考虑子树时无法通电的概率,它等于“所有儿子均不通电或儿子到这条边不导电”,转移方程是$f_x=(1-p_i)\prod (1-pre_k+pre_k*f_k)$,其中k为i的儿子,pre[k]为i到x的边导电的概率。

从父亲转移到儿子的概率同理,即满足“父亲在不考虑x子树的情况下不通电或父亲到x的边不到电”的概率,注意要除去x子树对f[fa]的影响。

方程是$f_x \times=1-pre_x+\frac{pre_x*f_{fa}}{1-pre_x+pre_x*f_x}$

注意判掉分母为零的情况。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 6 using namespace std;
 7 
 8 const int N=1000010,eps=1e-7;
 9 int n,u,v,cnt,to[N],nxt[N],h[N];
10 double val[N],w,ans,p[N],f[N];
11 void add(int u,int v,double w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; }
12 inline double Abs(double x){ return (x<0) ? -x : x; }
13 
14 void dfs(int x,int fa){
15     f[x]=1-p[x];
16     For(i,x) if ((k=to[i])!=fa) dfs(k,x),f[x]*=(f[k]-1)*val[i]+1;
17 }
18 
19 void dfs2(int x,int fa){
20     For(i,x) if ((k=to[i])!=fa){
21         if (Abs((f[k]-1)*val[i]+1)>eps) f[k]*=1-val[i]+val[i]*f[x]/((f[k]-1)*val[i]+1);
22         dfs2(k,x);
23     }
24 }
25 
26 int main(){
27     freopen("bzoj3566.in","r",stdin);
28     freopen("bzoj3566.out","w",stdout);
29     scanf("%d",&n);
30     rep(i,2,n) scanf("%d%d%lf",&u,&v,&w),add(u,v,w/100.),add(v,u,w/100.);
31     rep(i,1,n) scanf("%lf",&p[i]),p[i]/=100.;
32     dfs(1,0); dfs2(1,0);
33     rep(i,1,n) ans+=1-f[i];
34     printf("%.6lf\n",ans);
35     return 0;
36 }

 

转载于:https://www.cnblogs.com/HocRiser/p/9799784.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值