這道題求的是期望。
首先,一看到期望,就會想到可以將問題分成若干個子問題,再分開算期望,所以這道題可以使用動態規劃。
注意到每個葉子有房子的概率是均等的。所以答案就是遍歷每個葉子最少的步數/葉子的總數。
所以問題劃歸為求遍歷所有葉子的最少步數。
我們令fail[x]為以x為根的子樹找不到房子的最少步數。
su[x]為以x為根的子樹中找到房子最少步數。
le[x]為以x為根的子樹中葉子的個數。
則有 fail[x]=sigma(fail[y]+2);(worm[x]=false) fail[x]=0 (worm[x]=true);
su[x]+=(fail[x]+1)*le[y]+su[y];(其中fail[x]為在y之前沒有找到房子的步數)
顯然,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]。
所以可以根據這個排序。然後計算。
【總結】
求期望的題要注意搞清究竟要求什麼,不要盲目上手,要想方設法的將問題化繁為簡。像上述做法,整個過程中不涉及任何浮點運算,十分優秀。
【代碼】
[cpp]
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1005;
int su[N],fail[N],le[N],n,root;
bool w[N];
vector<int> a[N];
bool cmp(int x,int y)
{
return (fail[x]+2)*le[y]<(fail[y]+2)*le[x];
}
void dp(int x)
{
int i,y;
if (a[x].empty())
{
le[x]=1;
return;
}
for (i=0;i<a[x].size();i++)
{
y=a[x][i];
dp(y);
le[x]+=le[y];
}
sort(a[x].begin(),a[x].end(),cmp);
for (i=0;i<a[x].size();i++)
{
y=a[x][i];
su[x]+=(fail[x]+1)*le[y]+su[y];
fail[x]+=fail[y]+2;
}
if (w[x]) fail[x]=0;
}
int main()
{
int i,j;
char ch;
freopen("in","r",stdin);
while (1)
{
scanf("%d",&n);
if (n==0) break;
memset(fail,0,sizeof(fail));
memset(su,0,sizeof(su));
memset(le,0,sizeof(le));
memset(w,0,sizeof(w));
for (i=1;i<=n;i++)
a[i].clear();
for (i=1;i<=n;i++)
{
scanf("%d %c",&j,&ch);
if (j==-1) root=i;
else a[j].push_back(i);
w[i]=(ch=='Y'?true:false);
}
dp(root);
printf("%.4f\n",1.0*su[root]/le[root]);
}
}