51Nod 1378 树形DP+贪心

题目链接


题意:
给定一棵树和一个整数 k k k,可以在树的任意位置放置一个人,这个人可以监管距离其所在节点不超过 k k k的所有节点,问最少放置多少个人可以监管整棵树的所有节点。


思路:
此题的解题思路还挺具有启发意义。

对于树的相关问题,我们可以考虑先转换成线性问题来做,如果是线性的话,很显然最优解应该是从某一端的边界开始,每个长度为 2 k 2k 2k的区间的中心均放置一个人,若最后的距离不足 2 k 2k 2k,则也在其中心放置一个人。

我们可以推广贪心的思想到树形结构上:

对于这棵树,我们可以从叶子节点出发,定义 d p [ i ] dp[i] dp[i]
d p [ i ] > 0 dp[i]>0 dp[i]>0:则代表以 i i i为根节点的子树可以向上监管距离不超过 d p [ i ] dp[i] dp[i]的节点
d p [ i ] &lt; 0 dp[i]&lt;0 dp[i]<0:则代表以 i i i为根节点的子树需要其他节点帮助其监管距离为 − d p [ i ] -dp[i] dp[i]的节点

对于当前考虑的节点 i i i,我们可以求出其子节点 d p dp dp值的最小值 M n Mn Mn和最大值 M x Mx Mx,随后分以下情况讨论:
i i i为叶子节点,则说明需要其他节点帮助提供监管,更新 d p [ i ] = − 1 dp[i] = -1 dp[i]=1
M n = = − k Mn == -k Mn==k说明存在某一个以 i i i的子节点为根的子树,需要其他节点帮助提供 k k k个单位的监管距离,故由 i i i提供,更新 d p [ i ] = k dp[i] = k dp[i]=k
M n + M x − 1 &gt; = 0 Mn + Mx - 1 &gt;= 0 Mn+Mx1>=0 则说明有一个子树放置的人能够把其他全部需要监管的村庄都覆盖,则更新 d p [ i ] = M x − 1 dp[i] = Mx - 1 dp[i]=Mx1
若以上情况均不满足,则说明可以继续向上查找更优的放置点,更新 d p [ i ] = M n − 1 dp[i] = Mn - 1 dp[i]=Mn1

故此题得解。

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;

const int INF = 1e8 + 10;
const int A = 2e5 + 10;
class Gra{
public:
    int v,next;
}G[A<<2];
int head[A],dp[A],n,k,tot,ans;

void add(int u,int v){
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void dfs(int u,int pre){
    int Mn = INF,Mx = -INF;
    for(int i=head[u] ;i!=-1 ;i=G[i].next){
        int v = G[i].v;
        if(v == pre) continue;
        dfs(v,u);

        Mn = min(Mn,dp[v]);
        Mx = max(Mx,dp[v]);
    }

    if(Mn == INF) dp[u] = -1;
    else if(Mn <= -k){
        dp[u] = k;
        ans++;
    }
    else if(Mn + Mx > 0) dp[u] = Mx - 1;
    else dp[u] = Mn - 1;
}

int main(){
    memset(head,-1,sizeof(head));
    tot = 0;

    scanf("%d%d",&n,&k);

    if(k == 0) printf("%d\n",n);
    else{
        for(int i=1 ;i<n ;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }

        memset(dp,0,sizeof(dp));
        ans = 0;
        dfs(1,1);

        if(dp[1] < 0) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值