题意:
给出一颗有根树,边权均为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; }