LeetCode 1168. Optimize Water Distribution in a Village

题目描述:

There are n houses in a village. We want to supply water for all the houses by building wells and laying pipes.

For each house i, we can either build a well inside it directly with cost wells[i], or pipe in water from another well to it. The costs to lay pipes between houses are given by the array pipes, where each pipes[i] = [house1, house2, cost] represents the cost to connect house1 and house2 together using a pipe. Connections are bidirectional.

Find the minimum total cost to supply water to all houses.

 

Example 1:

Input: n = 3, wells = [1,2,2], pipes = [[1,2,1],[2,3,1]]
Output: 3
Explanation: 
The image shows the costs of connecting houses using pipes.
The best strategy is to build a well in the first house with cost 1 and connect the other houses to it with cost 2 so the total cost is 3. 

Constraints:

  • 1 <= n <= 10000
  • wells.length == n
  • 0 <= wells[i] <= 10^5
  • 1 <= pipes.length <= 10000
  • 1 <= pipes[i][0], pipes[i][1] <= n
  • 0 <= pipes[i][2] <= 10^5
  • pipes[i][0] != pipes[i][1]

题解:

1、wells数组可以看成是从村庄0到index处之间连接的代价

2、村庄(编号为1~n)有水只需要该村庄有通到村庄0的通路即可-即只需要把所有村庄连通即可,即形成一棵最小生成树,可采用Kruskal算法:即:

按照边的权重顺序(从小到大)将边加入生成树中,但是若加入该边会与生成树形成环则不加入该边。直到树中含有{\displaystyle V-1}V-1条边为止。这些边组成的就是该图的最小生成树。

Kruskal算法的时间复杂度为{\displaystyle E\log E}{\displaystyle E\log E}

3、判断两个村庄是否相连可采用并查集。Lee215的实现如下:

int[] uf;
    public int minCostToSupplyWater(int n, int[] wells, int[][] pipes) {
        uf = new int[n + 1];
        List<int[]> edges = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            uf[i + 1] = i + 1;
            edges.add(new int[] {0, i + 1, wells[i]});
        }
        for (int[] p : pipes) {
            edges.add(p);
        }
        Collections.sort(edges, (a, b) -> Integer.compare(a[2], b[2]));

        int res = 0;
        for (int[] e : edges) {
            int x = find(e[0]), y = find(e[1]);
            if (x != y) {
                res += e[2];
                uf[x] = y;
                --n;
            }
        }
        return res;
    }

    private int find(int x) {
        if (x != uf[x]) uf[x] = find(uf[x]);
        return uf[x];
    }

这种实现十分简洁。

我的实现套用了自己常用的一套模版的类WQUPC(weighted quick union with path comparess),显得有些冗余,不过能够提升性能。

class Solution {
    class WQUPC {
        int[] id;
        int[] sz;
        WQUPC(int n) {
            id = new int[n];
            sz = new int[n];
            for(int i = 0; i < n; i++) {
                id[i] = i;
                sz[i] = 1;
            }
        }
        int root(int i) {
            while(id[i] != i) {
                id[i] = id[id[i]];
                i = id[i];
            }
            return id[i];
        }
        boolean connected(int i, int j) {
            return root(i) == root(j);
        }
        boolean union(int i, int j) {
            int pi = root(i);
            int pj = root(j);
            if(pi == pj) return false;
            if(sz[pi] > sz[pj]) {
                id[pj] = id[pi];
                sz[pi] += sz[pj];
            } else {
                id[pi] = id[pj];
                sz[pj] += sz[pi];
            }
            return true;
        }
    }
    
    public int minCostToSupplyWater(int n, int[] wells, int[][] pipes) {
        List<int[]> edges = new ArrayList<>();
        for(int i = 0; i < wells.length; i++) {
            edges.add(new int[]{0, i + 1, wells[i]});
        }
        for(int[] pipe : pipes) {
            edges.add(new int[]{pipe[0], pipe[1], pipe[2]});
        }
        Collections.sort(edges, (a, b) -> Integer.compare(a[2], b[2]));
        WQUPC qu = new WQUPC(n + 1);
        int res = 0;
        for(int[] edge : edges) {
            if(qu.union(edge[0], edge[1])) {
                // System.out.println(edge[0] + " " + edge[1] + " " + edge[2]);
                res += edge[2];
            }
        }
        return res;
    }
}

 

转载于:https://www.cnblogs.com/rookielet/p/11415625.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值