数据结构实验之图论六:村村通公路

数据结构实验之图论六:村村通公路

Time Limit: 1000MS Memory Limit: 65536KB
Problem Description
当前农村公路建设正如火如荼的展开,某乡镇政府决定实现村村通公路,工程师现有各个村落之间的原始道路统计数据表,表中列出了各村之间可以建设公路的若干条道路的成本,你的任务是根据给出的数据表,求使得每个村都有公路连通所需要的最低成本。
Input
连续多组数据输入,每组数据包括村落数目N(N <= 1000)和可供选择的道路数目M(M <= 3000),随后M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个村庄的编号和修建该道路的预算成本,村庄从1~N编号。 
Output
输出使每个村庄都有公路连通所需要的最低成本,如果输入数据不能使所有村庄畅通,则输出-1,表示有些村庄之间没有路连通。 
Example Input
5 8
1 2 12
1 3 9
1 4 11
1 5 3
2 3 6
2 4 9
3 4 4
4 5 6
Example Output
19
Hint
 
Author
xam  
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
struct node {
    int u, v;
    int w;
}edge[3000+5];
int pre[1000+5];
int root(int x) {
    int y=x;
    while(x!=pre[x]) {
        x=pre[x];
    }
    while(y!=x) {
        int z=pre[y];
        pre[y]=x;
        y=z;
    }
    return x;
}
bool cmp(node a, node b) {
    return a.w<b.w;
}
int main() {
    int n, m;
    while(~scanf("%d %d", &n, &m)) {
        for(int i=1;i<=n;i++)
            pre[i]=i;
        for(int i=0;i<m;i++)
            scanf("%d %d %d", &edge[i].u, &edge[i].v, &edge[i].w);
        int ans=0;
        sort(edge,edge+m,cmp);
        for(int i=0;i<m;i++) {
            if(root(edge[i].u)!=root(edge[i].v)) {
                pre[root(edge[i].u)]=edge[i].v;
                ans+=edge[i].w;
            }
        }
        int cnt=0;
        for(int i=1;i<=n;i++)
            if(pre[i]==i) cnt++;
        printf("%d\n", cnt==1?ans:-1);
    }
    return 0;
}

### 实验题14:求解建公路问题(图论算法) #### 问题背景 在农村地区,为了实现村村通公路的目标,需要从多个候选道路中选择一部分进行修建,使得所有村庄都能互相连通,同时总建造成本最低。这是一个典型的最小生成树(Minimum Spanning Tree, MST)问题[^3]。 #### 解决方案概述 解决此类问题的核心在于寻找一种方法,能够在保证所有节点连通的前提下,选取权值和最小的一组边。常见的两种经典算法分别为 Prim 算法 和 Kruskal 算法。以下是这两种算法的具体介绍及其 Python 实现。 --- ### 方法一:Kruskal 算法 #### 思想描述 Kruskal 算法是一种基于贪心策略的解决方案,其基本思路如下: - 将所有的边按照权重从小到大排序; - 初始化每个顶点为独立的一个集合; - 按照顺序依次考虑每条边,如果这条边连接的两个顶点不属于同一个集合,则将其加入结果集中,并将这两个顶点所在的集合合并; - 重复上述过程直到结果集中的边数量等于顶点数减一为止[^4]。 #### 时间复杂度 假设共有 $E$ 条边和 $V$ 个顶点,则 Krusakl 算法的主要开销来自于对边的排序 ($O(E\log E)$) 及 Union-Find 数据结构的操作 ($O(\alpha(V))$, 其中 $\alpha$ 是反 Ackermann 函数)[^4]。 #### Python 实现 ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # Path compression return self.parent[x] def union(self, x, y): rootX = self.find(x) rootY = self.find(y) if rootX != rootY: self.parent[rootY] = rootX def kruskal(edges, N): """ 使用 Krusakl 算法计算最小生成树 :param edges: 边列表 [(u,v,w)], 表示 u-v 之间有一条权值 w 的边 :param N: 节点总数 :return: 最小生成树的总权重 或 -1 如果无法形成一棵完整的树 """ uf = UnionFind(N) total_weight = 0 mst_edges = [] # Sort edges by weight edges.sort(key=lambda e: e[2]) for edge in edges: u, v, w = edge if uf.find(u) != uf.find(v): # Check cycle condition uf.union(u, v) total_weight += w mst_edges.append(edge) # Ensure that we have exactly V-1 edges in our result if len(mst_edges) == N - 1: return total_weight else: return -1 # Not all nodes connected if __name__ == "__main__": sample_input = [ (1, 2, 12), (1, 3, 9), (1, 4, 11), (1, 5, 3), (2, 3, 6), (2, 4, 9), (3, 4, 4), (4, 5, 6) ] num_nodes = 5 cost = kruskal(sample_input, num_nodes) print("Total minimum cost:", cost) ``` --- ### 方法二:Prim 算法 #### 思想描述 Prim 算法同样利用了贪心思想,不过它采取的是逐步扩展已有部分的方式构建最小生成树: - 任选一个起点作为初始子图的一部分; - 不断从未被纳入的部分挑选与当前子图相连且具有最小权重的边加入子图; - 循环执行直至覆盖所有顶点[^4]。 #### 时间复杂度 使用邻接矩阵表示时,时间复杂度为 $O(V^2)$;而借助堆优化后的版本可达到 $O((V+E)\log V)$[^4]。 #### Python 实现 ```python import heapq def prim(graph, start_vertex): """ 使用 Prim 算法计算最小生成树 :param graph: 邻接表形式表示的加权无向图 {node: [(neighbor,weight)]} :param start_vertex: 起始节点 :return: 最小生成树的总权重 """ pq = [] # Priority queue of tuples (cost,node) visited = set() total_cost = 0 # Initialize with starting vertex and its outgoing connections for neighbor, cost in graph[start_vertex]: heapq.heappush(pq, (cost, neighbor)) while pq: current_cost, next_node = heapq.heappop(pq) if next_node not in visited: visited.add(next_node) total_cost += current_cost for new_neighbor, new_cost in graph[next_node]: if new_neighbor not in visited: heapq.heappush(pq, (new_cost, new_neighbor)) if len(visited) == len(graph): return total_cost else: return -1 # Graph is disconnected if __name__ == "__main__": adj_list_graph = { 1: [(2, 12), (3, 9), (4, 11), (5, 3)], 2: [(1, 12), (3, 6), (4, 9)], 3: [(1, 9), (2, 6), (4, 4)], 4: [(1, 11), (2, 9), (3, 4), (5, 6)], 5: [(1, 3), (4, 6)] } initial_vertex = 1 min_total_cost = prim(adj_list_graph, initial_vertex) print("Total minimum cost using Prim's algorithm:", min_total_cost) ``` --- ### 结果分析 无论是采用 Krusakl 还是 Prim 算法,最终都可以得到相同的最小生成树总权重。然而两者适用场景略有不同——当面对稀疏图时,由于 Krusakl 主要依赖于边的数量,因此表现更好;而对于稠密图来说,Prim 则更为高效[^4]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值