创建价值相同的连通块

98 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

创建价值相同的连通块

有一棵 n 个节点的无向树,节点编号为 0 到 n - 1 。

给你一个长度为 n 下标从 0 开始的整数数组 nums ,其中 nums[i] 表示第 i 个节点的值。同时给你一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 ai 与 bi 之间有一条边。

你可以 删除 一些边,将这棵树分成几个连通块。一个连通块的 价值 定义为这个连通块中 所有 节点 i 对应的 nums[i] 之和。

你需要删除一些边,删除后得到的各个连通块的价值都相等。请返回你可以删除的边数 最多 为多少。

  • 1 <= n <= 2 * 10^4
  • nums.length == n
  • 1 <= nums[i] <= 50
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= edges[i][0], edges[i][1] <= n - 1
  • edges 表示一棵合法的树。

思路

题目要求我们删边,这个比较抽象,不好理解 举一个例子来说明问题:

对于下列树来说:

image.png

我想让它连通块相等,首先,我们可以不删边,这样是一个连通块,一定相等其次,我们可以尝试把它分成2,3,4,...,n个连通块,看是否存在那么,分好的连通块一定要满足i=1nnums[i]可以被连通块的个数整除!那么,我们可以枚举一下,总和的因子数来确定每一个连通块内的值,当然,总和的因子数可以证明的是不会太多。那么,在确定了当前这个连通块应该具有多少值时,我们应该去判断是否合法即可!我想让它连通块相等,首先,我们可以不删边,这样是一个连通块,一定相等 \\ 其次,我们可以尝试把它分成 2, 3, 4, ..., n个连通块,看是否存在 \\ 那么,分好的连通块一定要满足 \sum_{i=1}^n nums[i] 可以被连通块的个数整除! \\ 那么,我们可以枚举一下,总和的因子数来确定每一个连通块内的值,\\ 当然,总和的因子数可以证明的是不会太多。 \\ 那么,在确定了当前这个连通块应该具有多少值时,我们应该去判断是否合法即可!\\

判断合法

判断合法如何做呢?从上往下分割好像不太好分割的样子,可是,我们可以转变一下思路从叶子节点回溯到根的路程中,只会发生三种情况:1.子节点值的和刚好满足划分值,删边2.子节点值的和大于划分值,划分值不满足3.子节点值的和小于划分值,继续累加到父节点去所以,我们只需要去搞一下这三种情况,我们就可以check是否满足条件的删边方案了!判断合法如何做呢? \\ 从上往下分割好像不太好分割的样子,可是,我们可以转变一下思路 \\ 从叶子节点回溯到根的路程中,只会发生三种情况:\\ 1. 子节点值的和刚好满足划分值,删边\\ 2. 子节点值的和大于划分值,划分值不满足 \\ 3. 子节点值的和小于划分值,继续累加到父节点去 \\ 所以,我们只需要去搞一下这三种情况,我们就可以check是否满足条件的删边方案了!

代码

class Solution {
public:
    vector<vector<int>> ve;
    vector<int> g;
    int cnt = 0;
    int flag = false;
    int now = 0;
    int dfs(int u, int f) {
        int sum = g[u];
        if (!flag) return 0;
        for (auto v : ve[u]) {
            if (v == f) continue;
            sum += dfs(v, u);
        }
        if (sum == now) {
            if (f != -1) cnt ++;
            return 0;
        } else if (sum > now) {
            flag = false;
            return 0;
        }
        return sum;
    }
    int componentValue(vector<int>& nums, vector<vector<int>>& edges) {
        int sum = 0;
        ve.resize(nums.size());
        g = nums;
        for (auto v : edges) ve[v[0]].push_back(v[1]), ve[v[1]].push_back(v[0]);
        for_each(begin(nums), end(nums), [&](int v) {sum += v;});
        int ans = 0;
        for (int i = 2; i <= nums.size(); i ++) {
            if (sum % i != 0) continue;
            now = sum / i;
            cnt = 0;
            flag = true;
            dfs(0, -1);
            if (flag) ans = max(ans, cnt);
        }
        return ans;
    }
};