poj 2057 树形DP+一点贪心
2014-08-08 20:55
387 查看
这是一道好题啊。感觉学到了不少思想和技巧。
下面参考了这个链接,我感觉讲清楚了: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]了。
先看图(用的是题目中的例子):
1
/ \
2 3(4)
/ \
4(1) 5(3)
先假设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]。
所以可以根据这个排序。
代码如下:
下面参考了这个链接,我感觉讲清楚了: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]了。
先看图(用的是题目中的例子):
1
/ \
2 3(4)
/ \
4(1) 5(3)
先假设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; }
相关文章推荐
- poj 2057 树形dp+贪心(蜗牛找家的期望值)
- poj 2057 树形dp 贪心
- POJ 2057 The Lost House 经典树形DP+贪心
- 【树形DP】【poj 2057】The Lost House
- poj 2057 树形DP,数学期望
- poj 2057 树形dp
- POJ 2057 The Lost Home 树形dp 难度:2
- poj 2057 树形dp
- poj 2057 树形dp ,特别考思路的好题。
- 【贪心】【树形DP】[POJ1463][HDU1054]Strategic game 战略游戏
- poj_2057 The Lost House(树形dp)
- poj 1463 树形dp或者贪心(树的最小点覆盖)
- poj 1849 贪心 ||树形dp
- POJ 2057 The Lost House (经典树形dp)
- pku 2057 The Lost House 树形dp+背包dp 解题报告
- poj 2342 树形DP
- 【LCA+树形DP】POJ 3417
- POJ 2037 今年暑假不AC【贪心+DP】
- poj 1155 树形DP 01背包
- 【树形dp】POJ 1463