设一棵树有两个子树A,B。Pa是房子在A子树上的概率,Pb是房子在B子树上的概率。
先走A子树寻找房子的期望是:
【在A子树上找到房子的路程】*Pa+(【没有在A子树上找到房子的路程(固定的一个值)】+【在B子树上找到房子的路程】)*Pb。
先走B子树寻找房子的期望是:
【在B子树上找到房子的路程】*Pb+(【没有在B子树上找到房子的路程(固定的一个值)】+【在A子树上找到房子的路程】)*Pa。
若这两个值做比较的话,可以化简为:
【没有在A子树上找到房子的路程(固定的一个值)】*Pb【没有在B子树上找到房子的路程(固定的一个值)】*Pa相比较。
Pa=(A子树上的叶子节点数)/(所有的叶子节点数);Pb也是如此。
通过排序就可以设计出在树上的寻找路径了。
——引用来自http://blog.csdn.net/lin375691011/article/details/31446343
当然,由于所有的叶子结点数目是固定的,所以可以直接用A子树上的叶子节点数代替Pa,类似的Pb也是如此。
对于表示u为根的子树上,找不到房子的步数和dp[u][0]=∑(dp[v][0]+2) ( (u,v)是一条边,因为是没找到,所以永远都要从哪里出发就回到那里,所以每多一层,就多一次去加一次来 )
(真的是一道非常不错的题目,值得深究)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define MAXN 1005 5 using namespace std; 6 int n,num[MAXN],dp[MAXN][2],head[MAXN]; 7 //dp[u][1]:表示u为根的子树上,成功找到房子的步数和 8 //dp[u][0]:表示u为根的子树上,找不到房子的步数和 9 //head[u]:第一条以u为头节点的边 10 bool worm[1005]; 11 struct Edge{ 12 int u,v,adj;//起点、终点、同起点的邻边 13 }edge[MAXN]; 14 bool cmp(int a,int b) 15 { 16 return( (dp[a][0]+2)*num[b] < (dp[b][0]+2)*num[a] ); 17 } 18 void dfs(int st) 19 { 20 int p=head[st]; 21 dp[st][0]=0; 22 dp[st][1]=0; 23 num[st]=0; 24 if(p==-1) num[st]++;//作为根,本身就是叶子结点 25 else 26 { 27 int cnt=0,group[10]; 28 while(p!=-1) 29 { 30 dfs(edge[p].v); 31 dp[st][0]+=dp[(edge[p].v)][0]+2; 32 num[st]+=num[edge[p].v]; 33 group[cnt++]=edge[p].v; 34 p=edge[p].adj; 35 } 36 if(worm[st]) dp[st][0]=0;//如果有worm帮忙,知道这里从这里往下不用再找了,没有房子,就不用跑 37 sort(group,group+cnt,cmp); 38 int tmp=0; 39 for(int i=0;i<cnt;i++)//i表示在第i棵子树下有房子 40 { 41 dp[st][1]+=dp[group[i]][1]+(tmp+1)*num[group[i]]; 42 tmp+=dp[group[i]][0]+2;//走了i子树,没有找到,返回到st 43 }//这一段for语句非常漂亮,属于解本题的核心思路 44 } 45 //printf("point %d: dp[][0]=%d dp[][1]=%d ",st,dp[st][0],dp[st][1]); 46 } 47 int main() 48 { 49 while(scanf("%d",&n) && n!=0) 50 { 51 memset(head,-1,sizeof(head)); 52 int from,cnt=1; 53 char tmp[5]; 54 scanf("%d%s",&from,tmp); 55 for(int i=2;i<=n;i++) 56 { 57 scanf("%d%s",&from,tmp); 58 worm[i]=(tmp[0]=='Y'?1:0); 59 edge[cnt].u=from; 60 edge[cnt].v=i; 61 edge[cnt].adj=head[from]; 62 head[from]=cnt; 63 cnt++; 64 } 65 /* 66 for(int i=1;i<=n-1;i++) printf("edge %d: from=%d to=%d adj=edge%d ",i,edge[i].u,edge[i].v,edge[i].adj); 67 for(int i=1;i<=n;i++) printf("head %d: edge %d ",i,head[i]); //检查边是否存储正确 68 for(int i=2;i<=n;i++) printf("point %d: worm %s ",i,worm[i]?"exist":"not exist"); //检查worm是否存储正确 69 */ 70 dfs(1); 71 printf("%.4f ", ((double)dp[1][1])/((double)num[1]) ); 72 } 73 }
另外,请注意http://poj.org/showmessage?message_id=341806,这影响到输入时的数据存储方式,之前我是按照:边是按顺序一层层下来的,就一直WA