题目大意:蜗牛把壳落在一个树的叶子结点了,壳在每个叶节点的概率相同,它在根节点处开始爬,可能在树杈处遇到虫子告诉他壳是否在这个子树中,每个节点间距离为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(有改动)
- #include <algorithm>
- #include <iostream>
- using namespace std;
- #define N 1005
- int next[N][N], cnt[N]; /* next[i][j] 表示节点i的第j个子节点。 cnt[i]记录节点i的子节点的个数 */
- bool worm[N]; // 记录有无虫子
- int n;
- struct TreeNode
- {
- int leaf, step1, step0; /* leaf表示该节点上的叶子节点个数。step1表示找到壳子所需步数。step0表示没找到壳子所需步数 */
- } node[N];
- bool cmp ( int a, int b )
- {
- return (node[a].step0 + 2) * node[b].leaf < (node[b].step0 + 2) * node[a].leaf;
- }
- void dfs ( int u )
- {
- if ( cnt[u] == 0 ) //u是叶子结点时
- {
- node[u].leaf = 1; //node相当于dp[][]数组
- node[u].step1 = node[u].step0 = 0; //相当于dp[u][1]与dp[u][0]
- return;
- }
- int i, v;
- for ( i = 1; i <= cnt[u]; i++ ) //求leaf和step0
- {
- v = next[u][i];
- dfs ( v );
- node[u].leaf += node[v].leaf;//u下的叶子个数等于u的子节点各自的叶子个数之和
- if ( worm[v] ) node[v].step0 = 0; /* 有虫子时,若v下的叶子节点上没有壳,直接返回,所以v结点找不到壳的路长为0 */
- node[u].step0 += node[v].step0 + 2; /* u->v, v->u, 所以要+2 */
- }
- /*排序,决定访问子节点的顺序,按照子节点找不到壳的路长从小到大排序。其实就是一个贪心的过程,每次选取的节点都能使重复的次数最小。 */
- sort ( next[u] + 1, next[u] + cnt[u] + 1, cmp );
- int leaf = 0, sum = 0; /* sum记录u为根,访问过的子树找不到壳的路长之和 */
- for ( i = 1; i <= cnt[u]; i++ )
- {
- v = next[u][i]; //下面的sum+1是因为要从u走到v
- node[u].step1 += (sum + 1) * node[v].leaf + node[v].step1;//前边与u相连的子树都没有找到壳,到v子树找到了,但由于先访问了那些边,所以成功找到壳的基础上,多出了前半个式子这么多的路长
- sum += node[v].step0 + 2; //+2是要回到u结点
- }
- }
- int main()
- {
- int n, x;
- char ch;
- while ( scanf("%d",&n) && n )
- {
- memset(worm,0,sizeof(worm));
- memset(cnt,0,sizeof(cnt));
- memset(node,0,sizeof(node));
- for ( int i = 1; i <= n; i++ )
- {
- scanf("%d %c",&x,&ch);
- if ( x != -1 )
- next[x][++cnt[x]] = i;
- if ( ch == 'Y' )
- worm[i] = 1;
- }
- dfs ( 1 );
- double ans = (double)node[1].step1 / node[1].leaf;
- printf("%.4lf\n", ans);
- }
- return 0;
- }