51Nod 1378 树形DP+贪心

题目链接


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


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

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

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

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

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

故此题得解。

代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/wubaizhe/article/details/80011337
今日推荐