这是一道好题啊。感觉学到了不少思想和技巧。
下面参考了这个链接,我感觉讲清楚了:http://blog.sina.com.cn/s/blog_5f5353cc0100hd08.html
由于总叶子数是固定的,那么要算最小期望,只要算出到各个叶结点的步数和的最小值即可。
success[i],表示在i为根的子树上成功找到House的步数和,当i为叶子时,Success[i]=0(这时一步都不用走)
failure[i],表示在i为根的子树上找不到House的步数,当worm[i]=1或者i为叶子时,那么Failure[i]=0(原因同上)
leaves[i],表示i为根的子树上叶子节点的数目,当为叶子时,Leaves[i]=0
在树的最低层i,自然Failure[i]=0,但它的father自然是Failure[father]=Failure[i]+2(走到I一步,走回去一步,所以是两步),而当结点father有worm时,则为Failure[father]=0;同样我们可以推出最树低的叶子Success[i]=0,而father与其他儿子有关,不单单就一个i儿子,那么画图,假设father有2个儿子,其中i儿子是失败的,那么另一个儿子k就是成功,但是先访问i,所以得出Success[father]=(Failure[father]+1)*Leaves[k]+Success[k]。比较费解的就是(Failure[father]+1)*Leaves[k]了。
先看图(用的是题目中的例子):
2
/
4(1)
先假设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前失败的步数).
显然,su[x]和x儿子的顺序是有很大关系的。
第一种想法是枚举所有的全排列。虽然每个节点只有最多8个儿子,但8!=40320,太大。
第二种想法是贪心。我们可以使用调整的思想来确定儿子的顺序。
设y1,y2为x的两个相邻的儿子。若y1在y2之前,则ans1=(fail[x]+1)*le[y1]+su[y1]+(fail[x]+2+fail[y1]+1)*le[y2]+su[y2]
若交换y1,y2,则有ans2=(fail[x]+1)*le[y2]+su[y2]+(fail[x]+2+fail[y2]+1)*le[y1]+su[y1]
则ans1-ans2=(fail[y1]+2)*le[y2]-(fail[y2]+2)-le[y1]。
所以可以根据这个排序。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1111
int tree[MAX][10]; //tree[k][i]表示第k个节点的第i个孩子。tree[k][0]表示节点k的孩子的个数。这个数组相当于建树
int fail[MAX],su[MAX],le[MAX];
bool worm[MAX];
int n,m;
char c;
bool cmp(int a,int b)
{
return (fail[a]+2)*le[b]<(fail[b]+2)*le[a];
}
void dp(int k) //自底向上递推
{
int i,j;
if(tree[k][0]==0) //叶子节点情况的初始化。
{
le[k]=1;
fail[k]=0;
su[k]=0;
return ;
}
for(i=1;i<=tree[k][0];i++) //初始化子节点
{
dp(tree[k][i]);
le[k]+=le[tree[k][i]];
}
sort(tree[k]+1,tree[k]+tree[k][0]+1,cmp); //贪心排序
for(i=1;i<=tree[k][0];i++)
{
int son=tree[k][i];
su[k]+=(fail[k]+1)*le[son]+su[son];
fail[k]+=fail[son]+2;
}
if(worm[k]) fail[k]=0;
}
int main()
{
int i,j;
while(scanf("%d",&n),n)
{
memset(worm,0,sizeof(worm));
memset(fail,0,sizeof(fail));
memset(su,0,sizeof(su));
memset(tree,0,sizeof(tree));
memset(le,0,sizeof(le));
for(i=1;i<=n;i++)
{
scanf("%d %c",&m,&c);
if(m!=-1)
tree[m][++tree[m][0]]=i;
if(c=='Y') worm[i]=1;
}
dp(1);
printf("%.4lf\n",1.0*su[1]/le[1]);
}
return 0;
}