动态规划-状态压缩-707. 最优账户结余

2020-04-07 16:52:12

问题描述:

给一有向图,每一条边用一个 三元组 表示, 比如 [u, v, w] 代表权值为 w 的从 u 到 v 的一条边. 计算出保证每个点的权重相等需要添加的最少的边数. 也就是说, 指向这一点的边权重总和等于这个点指向其他点的边权重之和.

样例

样例1

输入: [[0,1,10],[2,0,5]]
输出: 2
说明:
需要添加两条边
它们是 [1,0,5] 以及 [1,2,5]

样例2

输入: [[0,1,10],[1,0,1],[1,2,5],[2,0,5]]
输出: 1
说明:
只需要添加一条边 [1,0,4]

注意事项

  1. 注意 u ≠ v 且 w > 0
  2. 下标不一定是线性的, 比如 顶点下标可以为 0,1,2, 也可以为 0,2,6.

问题求解:

本题可以使用动态规划来进行求解,对于一个集合,如何其和为0,那么就是一个合法的子集,我们只需要去遍历其合法的子集组成就可以得到最终的结果。

时间复杂度:O(2 ^ n)

    public int balanceGraph(int[][] edges) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int[] e : edges) {
            int from = e[0];
            int to = e[1];
            int w = e[2];
            map.put(from, map.getOrDefault(from, 0) - w);
            map.put(to, map.getOrDefault(to, 0) + w);
        }
        int[] nums = new int[map.size()];
        int len = 0;
        for (int v : map.values()) {
            if (v != 0) {
                nums[len++] = v;
            }
        }
        if (len == 0) return 0;
        int[] dp = new int[1 << len];
        Arrays.fill(dp, Integer.MAX_VALUE / 2);
        for (int i = 1; i < dp.length; i++) {
            int sum = 0;
            int cnt = 0;
            for (int j = 0; j < len; j++) {
                if ((1 << j & i) != 0) {
                    sum += nums[j];
                    cnt += 1;
                }
            }
            if (sum == 0) {
                dp[i] = cnt - 1;
                for (int j = 1; j < dp.length; j++) {
                    if ((j & i) == j) {
                        dp[i] = Math.min(dp[i], dp[j] + dp[i - j]);
                    }
                }
            }
        }
        return dp[dp.length - 1];
    }

  

原文地址:https://www.cnblogs.com/hyserendipity/p/12654366.html