class Solution {
public:
int minCostToSupplyWater(int n, vector<int>& wells, vector<vector<int>>& pipes) {
}
};
1168. 水资源分配优化
村里面一共有 n
栋房子。我们希望通过建造水井和铺设管道来为所有房子供水。
对于每个房子 i
,我们有两种可选的供水方案:一种是直接在房子内建造水井,成本为 wells[i - 1]
(注意 -1
,因为 索引从0开始 );另一种是从另一口井铺设管道引水,数组 pipes
给出了在房子间铺设管道的成本,其中每个 pipes[j] = [house1j, house2j, costj]
代表用管道将 house1j
和 house2j
连接在一起的成本。连接是双向的。
请返回 为所有房子都供水的最低总成本 。
示例 1:
输入:n = 3, wells = [1,2,2], pipes = [[1,2,1],[2,3,1]] 输出:3 解释: 上图展示了铺设管道连接房屋的成本。 最好的策略是在第一个房子里建造水井(成本为 1),然后将其他房子铺设管道连起来(成本为 2),所以总成本为 3。
示例 2:
输入:n = 2, wells = [1,1], pipes = [[1,2,1]] 输出:2 解释:我们可以用以下三种方法中的一种来提供低成本的水: 选项1: 在1号房子里面建一口井,成本为1 在房子2内建造井,成本为1 总成本是2。 选项2: 在1号房子里面建一口井,成本为1 -花费1连接房子2和房子1。 总成本是2。 选项3: 在房子2内建造井,成本为1 -花费1连接房子1和房子2。 总成本是2。 注意,我们可以用cost 1或cost 2连接房子1和房子2,但我们总是选择最便宜的选项。
提示:
2 <= n <= 104
wells.length == n
0 <= wells[i] <= 105
1 <= pipes.length <= 104
pipes[j].length == 3
1 <= house1j, house2j <= n
0 <= costj <= 105
house1j != house2j
原站题解
golang 解法, 执行用时: 84 ms, 内存消耗: 7.2 MB, 提交时间: 2023-10-22 09:02:42
func minCostToSupplyWater(n int, wells []int, pipes [][]int) int { // 不能用 kruskal,因为至少要有一个 house 是用 well 的 // 所以不能一开始就将所有 pipe 加入优先队列 // 将邻接矩阵变为邻接链表 graph := make([][][]int, n) for i := 0; i < n; i++ { graph[i] = make([][]int, 0) } for _, pipe := range pipes { // 将所有 house idx 减 1 from, to, cost := pipe[0] - 1, pipe[1] - 1, pipe[2] graph[from] = append(graph[from], []int{to, cost}) graph[to] = append(graph[to], []int{from, cost}) } // 初始化 pq,只包括井 pq := Heap(make([][]int, 0)) for house, cost := range wells { heap.Push(&pq, []int{house, cost}) } visited := make([]bool, n) numVisited := 0 totalCost := 0 for numVisited < n { edge := heap.Pop(&pq).([]int) house, cost := edge[0], edge[1] // 因为刚开始所有的井都被加入了 // 而且通往一个 house 的边可能被重复加入 // 所以 pop 出来的 house 仍有可能重复 if visited[house] { continue } visited[house] = true numVisited++ totalCost += cost for _, edge := range graph[house] { if visited[edge[0]] { continue } heap.Push(&pq, edge) } } return totalCost } // 优先队列的每个元素是 长度为 2 的[]int // 0 元素是尚未通水的 house number,1 元素是 cost type Heap [][]int func (h Heap) Len() int {return len(h)} func (h Heap) Less(i, j int) bool {return h[i][1] < h[j][1]} // compare cost func (h Heap) Swap(i, j int) {h[i], h[j] = h[j], h[i]} func (h *Heap) Push(x interface{}) { *h = append(*h, x.([]int)) } func (h *Heap) Pop() interface{} { old := *h ret := old[len(old) - 1] *h = old[:len(old) - 1] return ret }
golang 解法, 执行用时: 56 ms, 内存消耗: 7.1 MB, 提交时间: 2023-10-22 09:02:32
import "sort" type UnionFind struct { Parent []int Cost []int } func (self *UnionFind) Init(n int, wells []int) { self.Parent = make([]int, n) self.Cost = make([]int, n) for i := 0; i < n; i++ { self.Parent[i] = i self.Cost[i] = wells[i] } } func (self *UnionFind) Find(i int) int { if self.Parent[i] == i { return i } tparent := self.Find(self.Parent[i]) self.Parent[i] = tparent self.Cost[i] = self.Cost[tparent] return tparent } func (self *UnionFind) Union(i, j, c int) int { p1 := self.Find(i) p2 := self.Find(j) if p1 == p2 { return 0 } if self.Cost[p1] <= self.Cost[p2] && self.Cost[p2] >= c { // keep i well save := self.Cost[p2] - c self.Parent[p2] = p1 self.Cost[p2] = self.Cost[p1] return save } else if self.Cost[p1] >= self.Cost[p2] && self.Cost[p1] >= c { // keep j well save := self.Cost[p1] - c self.Parent[p1] = p2 self.Cost[p1] = self.Cost[p2] return save } return 0 } type Pipes [][]int func (self Pipes) Len() int { return len(self) } func (self Pipes) Swap(i, j int) { self[i], self[j] = self[j], self[i] } func (self Pipes) Less(i, j int) bool { return self[i][2] < self[j][2] } func minCostToSupplyWater(n int, wells []int, pipes [][]int) int { cost := 0 for _, c := range wells { cost += c } uf := UnionFind{} uf.Init(n, wells) p := Pipes{} for i := 0; i < len(pipes); i++ { p = append(p, pipes[i]) } sort.Sort(p) pipes = p for _, pair := range pipes { house1 := pair[0]-1 house2 := pair[1]-1 c := pair[2] link := uf.Union(house1, house2, c) if link == 0 { continue } cost -= link } return cost }
cpp 解法, 执行用时: 100 ms, 内存消耗: 33.5 MB, 提交时间: 2023-10-22 09:01:38
class UnionFindMinTree2 { public: vector<pair<int, int>> nodes; UnionFindMinTree2(int n, vector<int>& wells) { nodes = vector<pair<int, int>>(n); for (int i = 0; i < n; ++i) nodes[i] = { i, wells[i] }; } pair<int, int> find(int& x) { if (nodes[x].first == x) return nodes[x]; return nodes[x] = find(nodes[x].first); } void unionEle(int& a, int& b) { pair<int, int> pa = find(a); pair<int, int> pb = find(b); if (pa.first != pb.first) nodes[pb.first] = pa; } }; class Solution { public: int minCostToSupplyWater(int n, vector<int>& wells, vector<vector<int>>& pipes) { sort(pipes.begin(), pipes.end(), [](vector<int>& a, vector<int>& b) { return a[2] < b[2]; }); UnionFindMinTree2 ufmt(n, wells); int result = 0; for (int i : wells) result += i; for (int i = 0; i < pipes.size(); ++i) { int x1 = pipes[i][0] - 1; int x2 = pipes[i][1] - 1; int cost = pipes[i][2]; pair<int, int> p1 = ufmt.find(x1); pair<int, int> p2 = ufmt.find(x2); if (p1 != p2) { if (p1.second <= p2.second && p2.second >= cost) { result = result - p2.second + cost; ufmt.unionEle(x1, x2); } else if (p1.second >= cost) { result = result - p1.second + cost; ufmt.unionEle(x2, x1); } } } return result; } };
python3 解法, 执行用时: 144 ms, 内存消耗: 23.4 MB, 提交时间: 2023-10-22 08:58:37
class Solution: def minCostToSupplyWater(self, n: int, wells: List[int], pipes: List[List[int]]) -> int: # 邻接表表示的双向图 graph = defaultdict(list) # 添加索引为0的虚拟顶点。 # 然后在每一栋房子上加一条边,按成本加权 for index, cost in enumerate(wells): graph[0].append((cost, index + 1)) # 将双向边添加到图中 for house_1, house_2, cost in pipes: graph[house_1].append((cost, house_2)) graph[house_2].append((cost, house_1)) # 用于维护已添加到的所有顶点的集合 # 最终的 MST(最小生成树)从顶点 0 开始。 mst_set = set([0]) # 堆维护要访问的边的顺序, # 从起源于顶点0的边开始。 # 注意:我们能够从任意节点开始。 heapq.heapify(graph[0]) edges_heap = graph[0] total_cost = 0 while len(mst_set) < n + 1: cost, next_house = heapq.heappop(edges_heap) if next_house not in mst_set: # 将新顶点添加到集合中 mst_set.add(next_house) total_cost += cost # 在下一轮扩大候选边的选择范围 for new_cost, neighbor_house in graph[next_house]: if neighbor_house not in mst_set: heapq.heappush(edges_heap, (new_cost, neighbor_house)) return total_cost
java 解法, 执行用时: 42 ms, 内存消耗: 46.8 MB, 提交时间: 2023-10-22 08:57:44
class Solution { public int minCostToSupplyWater(int n, int[] wells, int[][] pipes) { // 最小堆,以维护要访问的边的顺序。 PriorityQueue<Pair<Integer, Integer>> edgesHeap = new PriorityQueue<>(n, (a, b) -> (a.getKey() - b.getKey())); // 图在邻接表中的表示 List<List<Pair<Integer, Integer>>> graph = new ArrayList<>(n + 1); for (int i = 0; i < n + 1; ++i) { graph.add(new ArrayList<Pair<Integer, Integer>>()); } // 添加索引为0的虚拟顶点, // 然后在每一栋房子上加一条边,按成本加权 for (int i = 0; i < wells.length; ++i) { Pair<Integer, Integer> virtualEdge = new Pair<>(wells[i], i + 1); graph.get(0).add(virtualEdge); // 使用来自虚拟顶点的边初始化堆。 edgesHeap.add(virtualEdge); } // 将双向边添加到图中 for (int i = 0; i < pipes.length; ++i) { int house1 = pipes[i][0]; int house2 = pipes[i][1]; int cost = pipes[i][2]; graph.get(house1).add(new Pair<Integer, Integer>(cost, house2)); graph.get(house2).add(new Pair<Integer, Integer>(cost, house1)); } // 从虚拟顶点0开始探索 Set<Integer> mstSet = new HashSet<>(); mstSet.add(0); int totalCost = 0; while (mstSet.size() < n + 1) { Pair<Integer, Integer> edge = edgesHeap.poll(); int cost = edge.getKey(); int nextHouse = edge.getValue(); if (mstSet.contains(nextHouse)) { continue; } // 将新顶点添加到集合中 mstSet.add(nextHouse); totalCost += cost; // 扩大下一轮优势候选人的选择范围 for (Pair<Integer, Integer> neighborEdge : graph.get(nextHouse)) { if (!mstSet.contains(neighborEdge.getValue())) { edgesHeap.add(neighborEdge); } } } return totalCost; } } class Solution2 { private void dfs(List<Integer>[] adjList, int[] visited, int startNode) { visited[startNode] = 1; for (int i = 0; i < adjList[startNode].size(); i++) { if (visited[adjList[startNode].get(i)] == 0) { dfs(adjList, visited, adjList[startNode].get(i)); } } } public int countComponents(int n, int[][] edges) { int components = 0; int[] visited = new int[n]; List<Integer>[] adjList = new ArrayList[n]; for (int i = 0; i < n; i++) { adjList[i] = new ArrayList<Integer>(); } for (int i = 0; i < edges.length; i++) { adjList[edges[i][0]].add(edges[i][1]); adjList[edges[i][1]].add(edges[i][0]); } for (int i = 0; i < n; i++) { if (visited[i] == 0) { components++; dfs(adjList, visited, i); } } return components; } }