POJ 2057 The Lost House 经典树形DP+贪心

来源:互联网 发布:免费cms系统哪个好 编辑:程序博客网 时间:2024/06/06 01:58

题意:链接

方法:树形DP+贪心

解析:这是一道好题。

好首先要明确这题求的是什么?

名义上是期望值,而实际上就是找一条路径。什么路径呢?从根节点走遍所有的叶子节点所花费步数最短的路径。

明确了题意后该怎么做呢?

首先看我们需要什么?

目前有个根节点,我们需要知道从他向一个分支走,失败步数是多少,成功步数是多少?

那么怎么维护我们需要的东西呢?

首先我们先给他们起个名:suc,fai;

其次再给一个节点的叶子节点的个数起个名:son

起名完事之后我们就要更新了。

先谈叶子节点,显然叶子节点的suc[x]=0,fai[x]=0,son[x]=1;

之后就是向上更新了,son和fai也很好搞

对于son的更新son[fa]+=son[x];

对于fai的更新fai[fa]+=fai[x]+2(此时worm[x]=0);否则的话不用管。

对于最不好弄的suc更新:

首先对于我们当前讨论的要走的子节点pn,这时候,蜗牛已经经过了走p1~pn-1的失败的步数,所以这些失败的步数是要记录的,我们给它命名为cnt-fai,此时我们会多走几个cnt-fai呢?我们发现,一共会多走son[pn]个cnt-fai,然而每次走过一个叶子节点后,更新一次suc[x],然后再将其视为失败,返回,找下一个叶子节点,此时我们就会发现,再返回到px的时候,这个蜗牛还需要继续返回一层,即返回到目前的根节点x,多走出一步,多走多少个一步呢?仍然是son[pn]个,用公式来写就是这样:suc[x]+=(cntfai+1)son[pn]+suc[pn]

同时更新cntfai,cntfai+=fai[pn]+2这个2就是指去以及回来的两步

以上,dp的部分差不多搞定了,观察上面的式子发现,唯一不能确定的就是pn是什么鬼?

也就是说,对于一个根节点x我们按照什么顺序来讨论他的子节点会使得x的suc最小呢?

以下引用discuss里某神犇的证明

假设交换相邻的两颗子树的选择顺序,设P1,P2为选他们的概率,A1,A2为房子确实在上面所需的步数,B1,B2为实际上不在上面所需的步数,则
调整后:Delta=P1*A1+P2*(B1+A2)-P2*A2-P1*(B2+A1)=P2*B1-P1*B2
于是Delta<0 <=> B1/P1小于B2/P2
而题设情况即为Delta<0
于是应按照B/L排序 即遍历此子树所需步数/其所含叶子树

转化为公式呢?就是这个东西

(fai[u]+2)son[v]<(fai[v]+2)son[u]

其中u和v分别是两个子节点。

后记:这题是道好题,做完后能学到不少东西,值得一做,然而自己在做的时候对于这种双线的把握还不是很好,比较欠缺,什么鬼的排序根本没想到,自己对于这种难题的把握还是差很多啊!

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 1100using namespace std ;int n,cnt;bool col[N];int head[N],suc[N],fai[N],son[N];char s[5];struct node{    int to,next;}edge[N];void init(){    memset(head,-1,sizeof(head));    memset(col,0,sizeof(col));    memset(suc,0,sizeof(suc));    memset(fai,0,sizeof(fai));    memset(son,0,sizeof(son));    cnt=1;}int cmp(int u,int v){    return (fai[u]+2)*son[v]<(fai[v]+2)*son[u];}void edgeadd(int from,int to){    edge[cnt].to=to;    edge[cnt].next=head[from];    head[from]=cnt++;}void dfs(int u){    if(head[u]==-1)    {        suc[u]=0,son[u]=1,fai[u]=0;    }    int tmp[N],tot=0;    for(int i=head[u];i!=-1;i=edge[i].next)    {        int to=edge[i].to;        tmp[++tot]=to;        dfs(to);        son[u]+=son[to];        if(!col[u])fai[u]+=fai[to]+2;     }    sort(tmp+1,tmp+1+tot,cmp);    int cnt_fai=0;    for(int i=1;i<=tot;i++)    {        suc[u]+=(cnt_fai+1)*son[tmp[i]]+suc[tmp[i]];        cnt_fai+=fai[tmp[i]]+2;    }}int main(){    while(scanf("%d",&n)&&n!=0)    {        init();        for(int i=1;i<=n;i++)        {            int pre;            scanf("%d%s",&pre,s);            col[i]=s[0]=='Y'?1:0;            if(pre==-1)continue;            edgeadd(pre,i);        }        dfs(1);        printf("%.4lf\n",(double)suc[1]/(double)son[1]);    }}
0 0