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

题意:链接

方法:树形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 1100
using 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]);
    }
}
时间: 2024-10-31 13:10:36

POJ 2057 The Lost House 经典树形DP+贪心的相关文章

poj 1947 经典树形dp

经典树形dp:问在一棵树上最少删除多少条边可以分离出一个节点数为p的子树. 定义状态: dp[i][j]表示从i为根的子树上分离出一个节点数为j的子树的代价(最少需要删除的边数). 考虑i节点的每个儿子ii,ii可以选或者不选(切断),然后就转化成了背包问题. dp[u][j] = min( dp[u][j], dp[u][j - k] + dp[v][k] ); 1 #include <iostream> 2 #include <cstring> 3 #include <c

HDU 2196 Computer 经典树形DP

一开始看错题了,后来发现原来是在一颗带权的树上面求出距离每一个点的最长距离,做两次dfs就好,具体的看注释? #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #incl

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

poj 1655 and 3107 and 2378 树形dp(树的重心问题)

简单的树形dp,顺便学习了树的重心的概念,即以该点为根的树的最大子树的结点数最少. poj 1655: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 20001; 7 int head[N]; 8 int balance[N]; 9 int child[N]; 10 int n, e; 11 12 struct

POJ 2342 &amp;&amp;HDU 1520 Anniversary party 树形DP 水题

一个公司的职员是分级制度的,所有员工刚好是一个树形结构,现在公司要举办一个聚会,邀请部分职员来参加. 要求: 1.为了聚会有趣,若邀请了一个职员,则该职员的直接上级(即父节点)和直接下级(即儿子节点)都不能被邀请 2.每一个员工都有一个兴奋值,在满足1的条件下,要使得邀请来的员工的兴奋值最高 输出最高的兴奋值. 简单的树形DP dp[i][1]:表示以i为根的子树,邀请节点i的最大兴奋值 dp[i][0]:表示以i为根的子树,不邀请节点i的最大兴奋值 先根据入度找出整棵树的根节点, 然后一次DF

POJ 3398 / UVA 1218 Perfect Service 树形DP

树形DP Perfect Service Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1378   Accepted: 668 Description A network is composed of N computers connected by N ? 1 communication links such that any two computers can be communicated via a uniqu

一道树形DP+贪心题——FramCraft

题目大意: mhy住在一棵有n个点的树的1号结点上,每个结点上都有一个妹子. mhy从自己家出发,去给每一个妹子都送一台电脑,每个妹子拿到电脑后就会开始安装zhx牌杀毒软件,第i个妹子安装时间为. 树上的每条边mhy能且仅能走两次,每次耗费1单位时间.mhy送完所有电脑后会回自己家里然后开始装zhx牌杀毒软件. 卸货和装电脑是不需要时间的. 求所有妹子和mhy都装好zhx牌杀毒软件的最短时间. 分析一下题意: 树上的每条边mhy能且仅能走两次,这个有什么用? 很有用.自己想想!我不说了,模拟模拟

HDU 3534 Tree (经典树形dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3534 题意: 给你一棵树,问你有多少对点的距离等于树的直径. 思路: dp[i][0]表示在i的子树中 离i最远的距离,dp[i][1]是次远距离.   cnt[i][0]则是最远的点的数量,cnt[i][1]表示次远的数量. up[i]表示以i向上 离i最远的距离.   up_cnt[i]表示向上最远的数量. 写的有点麻烦,调试了2小时... 1 //#pragma comment(linker,

poj 1694 An Old Stone Game 树形dp

//poj 1694 //sep9 #include <iostream> #include <algorithm> using namespace std; const int maxN=256; int n; int tree[maxN][maxN]; int ans[maxN]; int cmp(int a,int b) { return a>b; } int dfs(int u) { int tmp[maxN],t=0; if(tree[u][0]==0) retur