POJ 2057 The Lost House

感想:基本看的是资料和解题报告才A出来的,实话说,这题真心不是树形dp的入门题,不骂人,和谐。  详情请参考黄劲松的《贪婪的动态规划》 ,如果没找到的话可以留言,我发给你。


题意:蜗牛的房子遗失在了一棵树的某个叶子结点上,它要从根结点出发开始寻找它的房子。有一些中间结点可能会住着一些虫子,这些虫子会告诉蜗牛它的房子是否在以这个中间结点为根的子树上,这样蜗牛就不用白跑路了。当然,如果有些结点没有住着虫子的话,那么可怜的蜗牛只有靠自己决定访问顺序来探索了。假设蜗牛走过一条边的耗费都是1,且房子遗失在每个叶子结点的概率都是相等的,请算出蜗牛找到他的房子的最小数学期望值。(就这么几句话,竟然这么一大篇英文)

思路: fb[i]表示蜗牛不在i为根的子树上的时候遍历该子树需要的时间

         fa[i]表示蜗牛的房子在i为根的子树上的期望和。    l[i]表示以i为根的子树上叶子节点的数目

题目的答案即为  : fa[root]/l[root];   

     u的孩子节点依次为s[1].......s[k]

     fa[u]的计算方式为:fa[\u] = 0; fb[u] = 0;

     for(i=1   to k)       //这里的值还需要有子节点遍历的顺序来决定

      {

     ① fa[u] = fa[u] + (fb[u]+1)*l[s[i]]+fa[s[i]]     //这里的fb[u]一直在更新,只是表示遍历前面子节点需要的时间

     ②fb[u] = fb[u] + fb[s[i]] + 2     

      }

  ②的形式大家应该都懂,主要是①,

转自风炎大神:所以得出Success[father]=(Failure[father]+1)*Leaves[k]+Success[k]。比较费解的就是用红色标出的那部分了。

先看图(用的是题目中的例子):

     1

    \

   3(4)

\

4(1)  5(3)

先假设3为根,那么遍历4和5的步数1+3=4;那么如果1是根,这时要遍历4和5的步数是2+4=6,多一条边,使到4到的步数从1变为2,使到5的步数从3变为4。加起来就比原来多了2。也就是说如果有N个叶结点的话,那么应该多1*N步了。那么如果先从1到2,再从2回到1,之后再走呢。这时应该多了failure[1到2]*N步了。这就解释了红色那部分(红色中的那个failure[father]从代码中可以看出是走K前失败的步数).

继续:

  可以看到  若将 子节点  位置改变, 则对于公式 后两项没有影响,

  所以对于 子节点 间位置关系, 我们仅需考虑第一项就可以了

    令

       为 两子节点  顺序放置时 值。

      为 两子节点  交换放置时 值。

    则 两值做差 得到:

      

           所以我们得出结论, 顺序位置 则只跟元素 的信息有关,于别的元素的排列情况无关,所以元素 是可比的。

           

/*
真心把我虐得好惨,一看题目就傻眼了,搞了半天看懂题意发现完全超出自己的实力之外,然后去看黄劲松写的《贪婪的动态规划》,我靠,
发现完全就是纯数学,搞了一下午就看懂了这个推理(里面一句话将我困住半天,真希望这些大神们写点注释)
 */
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
int n;
struct Node {
bool flag;
int fa,fb,l;//fa为成功的,fb为失败,l为叶子结点的个数
}T[N];

vector<int> Q[N];

bool cmp(int x,int y)
{
   return ((T[x].fb+2)*T[y].l < (T[y].fb+2)*T[x].l);//贪心的顺序
}

void dfs(int u)
{
    for(int i=0;i<Q[u].size();++i)
     dfs(Q[u][i]);
    T[u].fa = T[u].fb = T[u].l = 0;

    if(Q[u].size()==0) T[u].l = 1;
    if(Q[u].size() > 0) {
       sort(Q[u].begin(),Q[u].end(),cmp);

       for(int i=0;i<Q[u].size();++i)
       {
           int v = Q[u][i];
           T[u].fa += (T[u].fb+1)*T[v].l + T[v].fa;
           T[u].fb += T[v].fb + 2;
           T[u].l += T[v].l;
       }
       if(T[u].flag) T[u].fb = 0;
    }
}

int main(void)
{
    while(cin>>n,n)
    {
     for(int i=1;i<=n;++i)  Q[i].clear();
     for(int i=1;i<=n;++i)
     {
       char ch;
       int tmp;
      scanf("%d %c",&tmp,&ch);
      if(tmp!=-1)//-1表示的是root
      {
        Q[tmp].push_back(i);
      }
      T[i].flag = (ch=='N'?false:true);
     }
     dfs(1);
     double ans = 1.0*T[1].fa/T[1].l;
     printf("%.4llf\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值