题意:
给出一颗有根树,边权均为1;
一个S在根结点上,要找到在某个叶子结点上的它的房子;
有的结点上有w,可以告诉S当前结点的子树上是否有它的房子;
房子在每个叶子结点的概率相等,选择一种最佳的计划,来让S走的期望值最小;
题解:
为了这道题去补了补期望是个啥;
简单来说就是,数学期望值=∑ f[x] * p[x]
f[x]是变量的值,p[x]是变量的概率;
那么,在某种特定的行走计划中,对于某个叶子结点上有房子所需要走的路程,应当是一个定值;
而每个叶子结点的概率都是叶子结点的总数的倒数;
因此,求出一种行走计划,将所有的路程加起来,再除以叶子结点总数,就是题中所求的期望值;
问题现在是找到一种这样的计划,这个问题的子问题无后效性是很显然的;
那么对一个结点的两个儿子,在决策上应当先走那个呢?
先设状态fa[x]表示x的子树上有房子的期望值;
fb[x]表示x的子树上没有房子的期望值;
令x为父结点,y1,y2为子结点,le[x]指x结点的子树上的叶子数;
那么先走y1的答案就是:
fa[x]=fa[y1]+1*le[y1]+(fb[y1]+2+1)*le[y2]+fa[y2];
// 当在y1子树上答案是fa[y1]+le[y1]因为除了在子树上以外,还有从父亲x走到y1的一步;
// 而在y2上应该是计算了之前子树的(fb[x]+1)*le[y2]表示多走的路,然后再加上fa[y2];
fb[x]=fb[y1]+2+fb[y2]+2;
先走y2的答案是:
fa[x]=fa[y2]+1*le[y2]+(fb[y2]+2+1)*le[y1]+fa[y1];
fb[x]=fb[y2]+2+fb[y1]+2;
可见,fb是不受先走后走的影响的,而将两个决策做比较,令y1优于y2,化简可得;
(fb[y1]+2)*le[y2]<(fb[y2]+2)*le[y1];
于是就可以用这个规律来判断,并且在多个子结点也可以这么搞,以这个排序维护fa,fb就可以了;
结点上有w的时候需要一点特判,就是S到达时立刻可以知道这里有没有房子,所以这里的fb就是0;
然后输出fa[root]/le[root]就可以了,实际上只需要最后输出转化浮点型即可;
代码:
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1001
using namespace std;
vector<int>to[N];
int le[N],fa[N],fb[N];
bool is[N];
char str[10];
void init(int n)
{
for(int i=1;i<=n;i++) to[i].clear();
memset(fa,0,sizeof(fa));
memset(fb,0,sizeof(fb));
memset(le,0,sizeof(le));
memset(is,0,sizeof(is));
}
int cmp(int a,int b)
{
return (fb[a]+2)*le[b]<(fb[b]+2)*le[a];
}
void dfs(int x)
{
le[x]=0;
int i,y,cnt=0;
int son[9];
for(i=0;i<to[x].size();i++)
{
y=to[x][i];
dfs(y);
le[x]+=le[y];
son[++cnt]=y;
}
sort(son+1,son+1+cnt,cmp);
for(i=1;i<=cnt;i++)
{
fa[x]+=(fb[x]+1)*le[son[i]]+fa[son[i]];
fb[x]+=fb[son[i]]+2;
}
if(!le[x])
le[x]=1;
if(is[x])
fb[x]=0;
}
int main()
{
int n,m,i,j,k,x,y,root;
while(scanf("%d",&n)&&n)
{
init(n);
for(i=1;i<=n;i++)
{
scanf("%d%s",&x,str);
if(x!=-1)
to[x].push_back(i);
else
root=i;
if(str[0]=='Y')
is[i]=1;
}
dfs(root);
printf("%.4lf\n",((double)fa[root])/le[root]);
}
return 0;
}