最小生成树 Kruskal(克鲁斯卡尔)算法

Description

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。 

 

Input

测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N 
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。 

 

Output

对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。 

 

Sample Input

 

3 3 1 2 1 1 3 2 2 3 4 1 3 2 3 2 0 100

 

Sample Output

 

3 ?

 

AC代码:

#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int N=10000+20; int uset[N]; int ran[N]; struct Node {     int x,y,price; } s[N]; bool cmp(Node a,Node b) //按权值从小到大排序 {     return a.price<b.price; } void makeset(int n) //建立并查集 {     for(int i=1; i<=n; i++)         uset[i]=i,ran[i]=0; } int find(int x) {     while(uset[x]!=x)         x=uset[x];     return x; } void unionset(int x,int y) //按秩合并 {     int a=find(x),b=find(y);     if(a==b)         return ;     if(ran[a]>ran[b])         uset[b]=a;     else     {         if(ran[a]==ran[b])             ran[b]++;         uset[a]=b;     } } int kruskal(int n,int m) //克鲁斯卡尔算法 {     int res=0,i;     sort(s+1,s+n+1,cmp);     for(i=1; i<=n; i++)     {         if(find(s[i].x)!=find(s[i].y))         {             unionset(s[i].x,s[i].y);             res +=s[i].price;         }     }     return res; } int main() {     int n,m;     int i,j;     int ans=0;     while(scanf("%d%d",&n,&m)!=EOF&&n)     {         ans=0;         makeset(m);         for(i=1; i<=n; i++)             scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].price);         ans=kruskal(n,m);         for(i=2; i<=m; i++) //判断所有城市有没有连到一起 及测试连通性         {             if(find(1)!=find(i))             {                 ans=-1;                 break;             }         }         if(ans==-1)             printf("?\n");         else             printf("%d\n",ans);     }     return 0; }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个最小生成树问题,可以使用Kruskal算法或Prim算法来解决。 以下是使用Prim算法的示例代码: ```python import heapq def prim(edges, n): # 初始化 visited = [False] * n mst = [] heap = [(0, 0)] # (cost, node) while heap: cost, node = heapq.heappop(heap) if visited[node]: continue visited[node] = True mst.append((cost, node)) for c, nei in edges[node]: if not visited[nei]: heapq.heappush(heap, (c, nei)) return sum(cost for cost, _ in mst) # 示例输入: n = 5 edges = [ [(1, 1), (2, 3)], [(1, 0), (3, 4), (4, 2)], [(2, 1), (4, 5)], [(3, 1), (4, 3), (5, 4)], [(5, 3)], ] print(prim(edges, n)) # 输出 7 ``` 其中,`edges`为邻接表形式的图,`n`为节点数。每个元素为一个列表,包含该节点所连接的其他节点及其边权。在本例中,第一个列表表示节点0连接到节点1和节点3,边权分别为1和3。 ### 回答2: 首先,我们可以将整个地区看做一个图,每个村落代表一个节点,道路代表节点的边。根据题目描述,我们需要找到一种最小生成树的方式,即使用最少的费用连接所有的村落。 一种常用的解决这个问题的算法是Prim算法。以下是用于计算全地区畅通所需最低成本的具体步骤: 1. 初始化一个包含所有村落的集合V,一个初始化为空的集合E(用于存储选择的边),另外创建一个大小为n的数组cost[],用于存储从当前生成树到未添加的节点的最小路径。 2. 随机选择一个起始村落,将其添加到生成树中,并从集合V中移除。 3. 遍历集合V中的所有节点v,对于每个节点v,计算从生成树中的节点到v的路径cost[v],并找到最小的路径对应的边e。 4. 将边e添加到集合E中,并将节点v从集合V中移除。 5. 重复步骤3和步骤4,直到集合V为空。 6. 计算集合E中所有边的费用之和,即为全地区畅通所需的最低成本。 注意:如果题目要求输出具体的路径,可以在步骤4中记录每个节点v的来源节点,最后通过回溯的方式得到具体路径。 通过使用Prim算法,可以在O(n^2)的时复杂度内解决这个问题,其中n为村落数量。 ### 回答3: 假设偏远地区有n个村落,我们可以使用图的最小生成树算法来解决这个问题。下面是程序的伪代码: 1. 输入偏远地区村落数量n和每两个村落修建道路的费用cost[n][n](cost[i][j]表示村落i与村落j之修建道路的费用)。 2. 创建一个大小为n的布尔数组visited,表示每个村落的访问状态,初始时所有村落都未访问。 3. 将第一个村落设为起始村落,设置visited[0]为true。 4. 创建一个整型变量minCost,表示全地区畅通所需的最低成本,初始值为0。 5. 创建一个整型变量count,表示已访问的村落数量,初始值为1。 6. 当count小于n时,执行以下循环: - 创建两个整型变量minCostIndex和minCostValue,分别用于记录最小费用的村落下标和最小费用的值,初始值为-1。 - 遍历已访问的村落,对于每个已访问的村落i,遍历未访问的村落j,找到最小的cost[i][j]。 - 如果minCostValue为-1或者找到了比minCostValue小的费用,更新minCostValue和minCostIndex。 - 将minCostIndex对应的未访问村落设为已访问,增加count的值,更新minCost的值。 7. 输出minCost,即全地区畅通所需的最低成本。 这样,我们就可以通过以上的程序来计算出全地区畅通所需的最低成本。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值