poj2057——the lost house

本文介绍了一道经典的树形动态规划与贪心算法结合的问题——蜗牛寻找遗落的壳。通过分析如何确定子节点访问顺序以求得最小期望路径长度,并提供了详细的算法思路与实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:蜗牛把壳落在一个树的叶子结点了,壳在每个叶节点的概率相同,它在根节点处开始爬,可能在树杈处遇到虫子告诉他壳是否在这个子树中,每个节点间距离为1,问他找到壳的爬行距离的最小期望

输入:(包含几个数据集,第i个结点的符号为i,根节点的父亲为-1

            树的结点个数n

            第i个结点的父亲结点 是否有虫子(Y/N)

            结点个数n为0//表示输入结束

输出:爬行距离的最小期望值(保留小数点后四位的

分析:树形动态规划+贪心

           本题的关键是确定子节点的访问顺序,求出一个使到各个叶节点的总步数最小的顺序,最小总步数/叶子总数即为最小期望。

           到达某叶节点的路长=之前没找到壳的无用路长+根到这个叶节点的路长

           比较一个结点的任意两个子树A、B的期望值大小,就是比较:

           在A子树上找到房子的路长*pa+(没在A子树上找到房子的路长+在B子树上找到房子的路长)*pb

           和

           在B子树上找到房子的路长*pb+(没在B子树上找到房子的路长+在A子树上找到房子的路长)*pa

           也就是比较:没在A子树上找到房子的路长*pb  和  没在B子树上找到房子的路长*pa

           pi=i子树上的叶子个数/叶子总数

           动态规划思想:dp[u][0]为从u结点往下找不到壳的路长

                                    dp[u][1]为从u结点往下能找到壳的路长

           在本题中设为node[u].step1/0

代码:转载自http://blog.youkuaiyun.com/tsaid/article/details/6832314(有改动)

  1. #include <algorithm>  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. #define N 1005  
  6.   
  7. int next[N][N], cnt[N]; /* next[i][j] 表示节点i的第j个子节点。 cnt[i]记录节点i的子节点的个数 */  
  8. bool worm[N]; // 记录有无虫子  
  9. int n;  
  10.   
  11. struct TreeNode  
  12. {  
  13.     int leaf, step1, step0; /* leaf表示该节点上的叶子节点个数。step1表示找到壳子所需步数。step0表示没找到壳子所需步数 */  
  14. } node[N];  
  15.   
  16. bool cmp ( int a, int b )  
  17. {  
  18.     return (node[a].step0 + 2) * node[b].leaf < (node[b].step0 + 2) * node[a].leaf;  
  19. }  
  20.   
  21. void dfs ( int u )  
  22. {  
  23.     if ( cnt[u] == 0 )  //u是叶子结点时
  24.     {  
  25.         node[u].leaf = 1;  //node相当于dp[][]数组
  26.         node[u].step1 = node[u].step0 = 0;  //相当于dp[u][1]与dp[u][0]
  27.         return;  
  28.     }  
  29.     int i, v;  
  30.     for ( i = 1; i <= cnt[u]; i++ )  //求leaf和step0
  31.     {  
  32.         v = next[u][i];  
  33.         dfs ( v );  
  34.         node[u].leaf += node[v].leaf;//u下的叶子个数等于u的子节点各自的叶子个数之和
  35.         if ( worm[v] ) node[v].step0 = 0; /* 有虫子时,若v下的叶子节点上没有壳,直接返回,所以v结点找不到壳的路长为0 */  
  36.         node[u].step0 += node[v].step0 + 2; /* u->v, v->u, 所以要+2 */  
  37.     }  
  38.   
  39. /*排序,决定访问子节点的顺序,按照子节点找不到壳的路长从小到大排序。其实就是一个贪心的过程,每次选取的节点都能使重复的次数最小。 */  
  40.     sort ( next[u] + 1, next[u] + cnt[u] + 1, cmp );  
  41.   
  42.     int leaf = 0, sum = 0; /* sum记录u为根,访问过的子树找不到壳的路长之和 */  
  43.     for ( i = 1; i <= cnt[u]; i++ )  
  44.     {  
  45.         v = next[u][i];  //下面的sum+1是因为要从u走到v
  46.         node[u].step1 += (sum + 1) * node[v].leaf + node[v].step1;//前边与u相连的子树都没有找到壳,到v子树找到了,但由于先访问了那些边,所以成功找到壳的基础上,多出了前半个式子这么多的路长
  47.         sum += node[v].step0 + 2;  //+2是要回到u结点
  48.     }  
  49. }  
  50.   
  51. int main()  
  52. {  
  53.     int n, x;  
  54.     char ch;  
  55.     while ( scanf("%d",&n) && n )  
  56.     {  
  57.         memset(worm,0,sizeof(worm));  
  58.         memset(cnt,0,sizeof(cnt));  
  59.         memset(node,0,sizeof(node));  
  60.   
  61.         for ( int i = 1; i <= n; i++ )  
  62.         {  
  63.             scanf("%d %c",&x,&ch);  
  64.             if ( x != -1 )  
  65.                 next[x][++cnt[x]] = i;  
  66.             if ( ch == 'Y' )  
  67.                 worm[i] = 1;  
  68.         }  
  69.         dfs ( 1 );  
  70.         double ans = (double)node[1].step1 / node[1].leaf;  
  71.         printf("%.4lf\n", ans);  
  72.     }  
  73.     return 0;  
  74. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值