hdu 4035

题意:有很多个房间,你可以经过传送门,传送方法有三种,1:到达与这个房间相连的其他房间,2:kill,回到起点。3:出口,逃离这个空间。这三个的发生概率是不同的,求逃离的数学期望。

显然是dp,可惜我对不起林教,没有得其真传,然后去搜报告,结果还是林教的报告,写的很简单,但是很明了,和以前那题差不多,可惜这是在这颗树上。然后wa了10次,分析问题太不全面了,竟然认为每个节点的A,B,C是不变的。。。我汗啊,不知道当时我是怎么想的。

分析一下:首先,期望一般采用逆推,对于每个节点,有三种转移1:到达与这个房间相连的其他房间,2:kill,回到起点。3:出口,逃离这个空间。用p[i]表示距离出口还差多少期望。(因为是反过来的)显然p[1] 为最终的答案。

那么列出每个节点的期望转移。

先是叶子节点:p[i]=p[1]*k[i]+e[i]*0+(1-k[i]-e[i])*p[f[i]];(0是因为当前节点为出口时期望为0,看清楚p[i]的定义;f[i]表示i当前的父亲节点);

然后是非叶子节点p[i]=p[1]*k[i]+e[i]*0+(1-k[i]-e[i])/cnt*((sum(p[j])+1)+p[f[i]]+1);(j为儿子节点,cnt为与这个房间相连的房子总数);其实我觉得这个方程设定也很巧妙,为什么要分开成父亲节点和儿子节点呢?特别是下面的转换;

设p[i]满足p[i]=A[i]*p[1]+B[i]*p[f[i]]+C[i];然后代入,将p[j]换掉;

你会得到一个关于p[i],e1,p[f[i]],的方程,自己动手算算;

这里我只给出化简后的式子。

p[i]=(k[i]+(1-k[i]-e[i])/m*sum(A[j]))/(1-(1-k[i]-e[i])/m*sum(B[j]))*p[1]+(1-k[i]-e[i])/m)/(1-(1-k[i]-e[i])/m*sum(B[j]))*p[f[i]]+1-k[i]-e[i]+(1-k[i]-e[i])/m*sum(C[j]));

p[i]=A[i]*e1+B[i]*p[f[i]]+C[i]对比得出

A[i]=(k[i]+(1-k[i]-e[i])/m*sum(A[j]))/(1-(1-k[i]-e[i])/m*sum(B[j]));

B[i]=(1-k[i]-e[i])/m)/(1-(1-k[i]-e[i])/m*sum(B[j]));

C[i]=1-k[i]-e[i]+(1-k[i]-e[i])/m*sum(C[j]));

这样就差不多完工了,因为p[1]=A[1]*P[1]+B[1]*0+C[1];

得出p[1]=C[1]/(1-A[1]);你会发现所有的p[i]其实都是中间变量,到最后都没了,林教说一般都是这样子的。

Run ID Submit Time Judge Status Pro.ID Exe.Time Exe.Memory Code Len. Language Author
4616318 2011-09-17 11:12:06 Accepted 4035 93MS 1020K 1658B C++ xym2010

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    double e,k;
}d[10005];
struct egde
{
    int v,next;
}ee[20005];
double A[10005],B[10005],C[10005];
int n,fa[10005],pos=0,flag=1;
void DP(int t,int f)
{
    int cnt=0;
    if(ee[fa[t]].next==-1&&f!=-1)
    {
        A[t]=d[t].k;
        B[t]=1-d[t].k-d[t].e;
        C[t]=1-d[t].k-d[t].e;
        return ;
    }
	A[t]=0;B[t]=0;C[t]=0;
    for(int i=fa[t];i!=-1;i=ee[i].next)
    {
        cnt++;
        if(ee[i].v==f)continue;
        DP(ee[i].v,t);
        A[t]+=A[ee[i].v];
        B[t]+=B[ee[i].v];
        C[t]+=C[ee[i].v];
    }
    double tt=(1-d[t].k-d[t].e)/cnt;
    A[t]=(d[t].k+tt*A[t])/(1-tt*B[t]);
    C[t]=(1-d[t].k-d[t].e+tt*C[t])/(1-tt*B[t]);
    B[t]=(tt)/(1-tt*B[t]);
}
int main()
{
    int T,t,x,y,k,e;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        memset(fa,-1,sizeof(fa));
        pos=0;flag=1;
        scanf("%d",&n);
        for(int i=0;i<n-1;i++)
        {
            scanf("%d%d",&x,&y);
            ee[pos].v=y;ee[pos].next=fa[x];fa[x]=pos++;
            ee[pos].v=x;ee[pos].next=fa[y];fa[y]=pos++;
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&k,&e);
            d[i].e=e/100.0;
            d[i].k=k/100.0;
        }
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(C,0,sizeof(C));
        DP(1,-1);
        printf("Case %d: ",t);
        if(fabs(1-A[1])<=1e-10)printf("impossible\n");
        else
            printf("%f\n",C[1]/(1-A[1]));
    }
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值