题意分析
- 给你一棵树,需要我们将这棵树划分为k个联通块,同时每个联通块的节点的权值的和都要大于等于给定的值,问是否可以达到目的。
思路分析
- 我们观察得到,为了确保是联通块,而且达到贪心的目的,我们应该从树的叶子节点往上面进行扩展。如果某一个节点和他的子树可以构成一个满足题目条件的联通块。那么我们就将这棵树和他的结点的权值都置为0.继续向上进行扩展。这是一个树形DP的思想。当然,也可以用BFS进行处理。 
- 如下图 
 
解法一 DFS(DP)
- 根据上面的分析,我们可以很好的写出这个DP函数,首先,从根节点开始进入,然后,处理字节点,同时判断此时的子节点是否可以构成一个满足条件的联通块。可以就将权值都置为0. 
- 代码如下 - 需要遍历所有的结点以及他们的连边。所以,时间复杂度为
- 需要存储所有的边的情况以及权值情况。所以空间复杂度为
 
- 需要遍历所有的结点以及他们的连边。所以,时间复杂度为
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param k int整型 
     * @param m int整型 
     * @param u int整型vector 
     * @param v int整型vector 
     * @param x int整型vector 
     * @return bool布尔型
     */
    vector<int> node[200010];
    int sum[200010];
    int res=0;
    // 树形DP
    void dp(int x,int fa,int m,vector<int>& tmp){
        sum[x]=tmp[x-1];
        for(auto y:node[x]){
            // 遍历所有的连边的结点
            if(y==fa) continue;
            dp(y,x,m,tmp);
            sum[x]+=sum[y];
        }
        // 如果最后这个结点和它的子树构成的金币的和大于等于m,那么就将这一块进行合并
        if(sum[x]>=m){
            sum[x]=0;
            res++;
        }
    }
    bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) {
        // write code here
        // 存储这颗树
        for(int i=0;i<n-1;i++){
            node[v[i]].push_back(u[i]);
            node[u[i]].push_back(v[i]);
        }
        dp(1,0,m,x);
        return res>=k;
    }
};解法二 BFS
- 我们上面知道,我们需要从叶子节点开始向上进行扩展。那么我们如何知道叶子节点在什么位置呢?有一个好的方法是计算每个节点的入度,入度为1的就说明是叶子节点了。然后,我们将这些节点放到队列里面,不断出队往根节点的方向进行扩展。同时维护满足条件的联通块的个数。 
- 代码如下 - 队列可能会存储n个节点的情况,需要处理n个节点的情况,时间复杂度为. 
- 队列存储了n个节点的情况,而且会使用到一些数组维护权值的情况,空间复杂度为
 
- 队列可能会存储n个节点的情况,需要处理n个节点的情况,时间复杂度为
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param k int整型 
     * @param m int整型 
     * @param u int整型vector 
     * @param v int整型vector 
     * @param x int整型vector 
     * @return bool布尔型
     */
    vector<int> node[200010];
    int sum[200010],in[200010];
    bool vis[200010]={false};
    bool solve(int n, int k, int m, vector<int>& u, vector<int>& v, vector<int>& x) {
        // write code here
        // 存储这颗树
        for(int i=0;i<n-1;i++){
            node[v[i]].push_back(u[i]);
            in[v[i]]++;
            node[u[i]].push_back(v[i]);
            in[u[i]]++;
        }
        queue<int> q;
        // 先把叶子结点放到放进队列里面,从树的底部一层一层扩展到根结点
        for(int i=1;i<=n;i++){
            sum[i]=x[i-1];
            if(in[i]==1){
                q.push(i);
                vis[i]=true;
            }
        }
        int res=0;
        while(!q.empty()){
            int now=q.front();
            q.pop();
            // 如果满足题目的条件,那么就执行合并的操作
            if(sum[now]>=m){
                res++;
                sum[now]=0;
            }
            for(auto y:node[now]){
                // 父节点合并子节点的权值
                sum[y]+=sum[now];
                // 如果这个结点没有被合并过
                if(!vis[y]){
                    vis[y]=true;
                    q.push(y);
                }
            }
        }
        return res>=k;
    }
};
 京公网安备 11010502036488号
京公网安备 11010502036488号