您的位置:首页 > 其它

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]。

所以可以根据这个排序。

代码如下:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: