POJ - 2057 The Lost House(树形DP+贪心)

https://vjudge.net/problem/POJ-2057

题意

有一只蜗牛爬上某个树枝末睡着之后从树上掉下来,发现后面的"房子"却丢在了树上面,。现在这只蜗牛要求寻找它的房子,它又得从树根开始爬起去找房子。现在要求一条路径使得其找到房子所要爬行的期望距离最小。房子等可能的出现在每个树枝末。某些分叉点会有虫子,会告知上次是否路过此地。

分析

有了虫子的存在,会影响我们遍历这棵树的路径,当虫子给出的信息是N时,就没必要走这个子树。所以问题就是设计一个期望值最小的路径。

设从root结点出发,有两个子树A和B,PA是房子在A子树上的概率,PB同理。那么根据遍历顺序不同,得出以下两个式子:

先A后B的期望:【在A子树上找到房子的总路程】*PA+(【在A子树上没有找到房子的总路程】+【在B子树上找到房子的总路程】)*PB

先B后A的期望:【在B子树上找到房子的总路程】*PB+(【在B子树上没有找到房子的总路程】+【在A子树上找到房子的总路程】)*PA

其中没有找到房子的总路程是固定值,即为遍历整个子树的路程(根据虫子的信息)。

那么答案要求期望最小,需要贪心地遍历子树,上述两个式子一比较,

则变成了【在A子树上没有找到房子的总路程】*PB【在B子树上没有找到房子的总路程】*PA的比较,于是只用将子树进行排序,然后按照此顺序遍历子树必定是最优的了。

 

设dp[i][0]表示以i为根的子树找不到房子时要爬行总路程。dp[i][1]表示以i为根的子树在选择好所有分支点的爬行方案后,枚举完房子落在该子树所有叶子节点上的总爬行距离的最小值。

那么有动态方程:

num[x]表示x节点一共有多少个孩子节点

dp[i][0] = sum{ dp[j][0] + 2 } 当i没有毛毛虫且要求j是i的孩子节点, 多出来的2就是连接该孩子节点的边来回的两次

dp[i][0] = 0 当该点有毛毛虫的时候, 原因是因为毛毛虫会告诉我们这点下面没有房子

当一个节点时叶子节点的时候,那么 dp[i][0] = dp[i][1] = 0;

要明确dp[i][1]是表示在遇到分支选择的先后顺序决定后,我们枚举房子在其各个叶子 上的所要爬行的总距离

dp[i][1] = sum{ (sum{dp[1..j-1][0]+2}+1}*num[j] + dp[j][1]}, 其中j是i的孩子

上面的意思翻译过来就是遍历i的孩子中的j号子树所有叶子节点所 要爬行的最短距离,其值就是: 前面[1, j-1]号子树没有找到节点所爬行的最短距离加上走了的多余的边再加上遍历j 号子树所有叶子节点所爬行的最短距离。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define ms(a, b) memset(a, b, sizeof(a))
#define pb push_back
#define mp make_pair
#define pii pair<int, int>
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define random(a, b) rand()*rand()%(b-a+1)+a
#define pi acos(-1.0)
//const ll INF = 0x3f3f3f3f3f3f3f3fll;
const int inf = 0x3f3f3f3f;
const int maxn = (1<<18) +10;
const int maxm = 1e5 +10;
const ll mod = 1e9+7;
const double eps = 1e-9;
ll qpow(ll a,ll b){ ll res=1; for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod; return res;}

struct ND{
    int to,nxt;
}edge[1010];
int n;
int head[1010],num[1010],tot;
bool vis[1010];
int dp[1010][2];
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].nxt=head[u];
    head[u]=tot++;
}
void init(){
    ms(head,-1);
    ms(vis,false);
    tot=0;
}
int cmp(int a,int b){
    return (dp[a][0]+2)*num[b]<(dp[b][0]+2)*num[a];
}
void dfs(int u){
    int p = head[u];
    dp[u][0]=dp[u][1]=0;
    num[u]=0;
    if(p==-1){
        num[u]++;
    }else{
        int cnt=0,s[10];
        while(p!=-1){
            s[cnt++]=edge[p].to;
            dfs(edge[p].to);
            num[u]+=num[edge[p].to];
            if(!vis[u]) dp[u][0]+=dp[edge[p].to][0]+2;
            p=edge[p].nxt;  
        }
        sort(s,s+cnt,cmp);
        int tmp=0;
        for(int i=0;i<cnt;i++){
            dp[u][1]+=dp[s[i]][1]+(tmp+1)*num[s[i]];
            tmp+=dp[s[i]][0]+2;
        }
    }
}
int main(){
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    int fa;
    char c;
    while(~scanf("%d",&n)&&n){
        init();
        scanf("%d %c",&fa,&c);
        for(int i=2;i<=n;i++){
            scanf("%d %c",&fa,&c);
            addedge(fa,i);
            if(c=='Y') vis[i]=true;
        }
        dfs(1);
        printf("%.4f\n",1.0*dp[1][1]/num[1]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/fht-litost/p/9471692.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值