• 最小生成树算法


    目录

    模板例题

    输入描述

    输出描述

    输入输出样例

    运行限制

    prim算法

    c

    python

    Kruskal算法

    c++

    python


    模板例题

    L 城一共有 N 个小区。

    小明是城市建设的规划者,他计划在城市修 M 条路,每修建一条路都要支付工人们相应的工钱(需要支付的工钱 = 路的长度)。

    然而小明所拿到的经费并不够支付修建 M 条路的工钱,于是迫于无奈,他只能将计划改变为修建若干条路,使得 N 个小区之间两两联通。

    小明希望尽量剩下更多的经费投入到别的项目中,因此请你通过程序帮他计算出完成计划所需的最低开销。

    输入描述

    输入第一行包含三个正整数 N,M。

    第 2到 M + 1 行每行包含三个正整数 u,v,w,表示 u↔v 之间存在一条距离为 w 的路。

    1≤N≤10^5,0≤m≤3×10^5,1≤ui​,vi​≤N,0≤wi​≤10^9。

    输出描述

    输出占一行,包含一个整数,表示完成计划所需的最低开销。

    若无法完成计划,则输出 -1。

    输入输出样例

    示例 1

    输入

    1. 5 6
    2. 1 2 2
    3. 1 3 7
    4. 1 4 6
    5. 2 3 1
    6. 3 4 3
    7. 3 5 2

    输出

    8
    

    运行限制

    • 最大运行时间:3s
    • 最大运行内存: 256M

    prim算法

    c++

    pii第一个值是边的权重,第二个值是这条边上的一个点的编号

    1. #include <bits/stdc++.h>
    2. using namespace std;
    3. int n,m;
    4. const int N=100010;
    5. const int INF=0x3f3f3f;
    6. bool vis[N]; // =ture: 表示点i已经在MST中
    7. int dis[N];
    8. typedef pair<int,int> pii;
    9. vector<pii> g[N];
    10. priority_queue<pair<int,int>,vector<pii>,greater<pii> > q; //优先队列
    11. void prim(int s){
    12. memset(dis,INF,sizeof(dis));
    13. q.push({0,s}); //从s点开始处理队列
    14. dis[s]=0;
    15. long long ans=0; // 答案可能很大,要开long long
    16. while(!q.empty()) {
    17. int u=q.top().second; //pop出距集合最近的点u
    18. q.pop();
    19. if(vis[u]) continue; //丢弃已经在MST中的点,有判圈的作用
    20. vis[u]=1;
    21. ans+=dis[u];
    22. for(int i=0;i<g[u].size();i++){ //检查点u的所有邻居
    23. pii v=g[u][i]; //一个邻居
    24. if(!vis[v.second])
    25. if(v.first<dis[v.second]){
    26. dis[v.second]=v.first;
    27. q.push(pii(dis[v.second],v.second));//扩展新的邻居,放进优先队列
    28. }
    29. }
    30. }
    31. for(int i=1;i<=n;i++)
    32. if(!vis[i]){
    33. cout<<"-1"<<endl;
    34. return ;
    35. }
    36. cout<<ans<<endl;
    37. }
    38. int main(){
    39. cin>>n>>m;
    40. for(int i=0;i<m;i++){
    41. int x,y,z;
    42. scanf("%d%d%d",&x,&y,&z);
    43. g[x].push_back({z,y}); //双向边
    44. g[y].push_back({z,x});
    45. }
    46. prim(1);
    47. return 0;
    48. }

    python

    在python代码中,通过head列表实现对结点i的邻居结点的访问

    1. from heapq import heappush, heappop
    2. def add(u, v, w):
    3. global k
    4. edges[k][0] = v
    5. edges[k][1] = w
    6. edges[k][2] = head[u]
    7. head[u] = k
    8. k += 1
    9. def prime():
    10. global cnt, ans
    11. dis = [float('inf') for _ in range(n + 1)]
    12. dis[1] = 0
    13. q = []
    14. vis = [False for _ in range(n + 1)] # =ture: 表示点i已经在MST中
    15. heappush(q, (0, 1)) #从s点开始处理队列
    16. while q and cnt < n:
    17. w, u = heappop(q) #pop出距集合最近的点u
    18. if not vis[u]:
    19. vis[u] = True
    20. ans += w
    21. i = head[u]
    22. cnt += 1
    23. while i: #检查点u的所有邻居
    24. v = edges[i][0]
    25. if dis[v] > edges[i][1]:
    26. dis[v] = edges[i][1]
    27. heappush(q, [dis[v], v])#扩展新的邻居,放进优先队列
    28. i = edges[i][2]
    29. n, m = map(int,input().split())
    30. edges = [[0, 0, 0] for i in range(2 * m + 1)]
    31. head = [0 for i in range(n + 1)]
    32. k = 1
    33. ans, cnt = 0, 0
    34. for i in range(m):
    35. u, v, w = map(int,input().split())
    36. add(u, v, w) #双向边
    37. add(v, u, w)
    38. prime()
    39. if cnt != n: print('-1')
    40. else: print(ans)

    Kruskal算法

    c++

    在c++代码中通过已经覆盖的点的数量判断是否可以生成最小树

    1. #include<bits/stdc++.h>
    2. using namespace std;
    3. const int N = 1e5+1,M = 3e5+1;
    4. int n,m,cnt;
    5. long long ans;
    6. int s[N];//并查集
    7. struct Edge{ int from,to,dis;}edge[M]; //用最简单且最省空间的结构体数组存边
    8. bool cmp(Edge a,Edge b){ //从小到大排序
    9. return (a.dis<b.dis);
    10. }
    11. int find(int x) { //查询并查集,返回x的根
    12. if(x!=s[x]) s[x]=find(s[x]);//路径压缩
    13. return s[x];
    14. }
    15. void union_set(int x,int y) { //合并
    16. s[find(y)]=find(x);
    17. }
    18. int main(){
    19. cin>>n>>m;
    20. for(int i=1;i<=m;++i)
    21. cin>>edge[i].from>>edge[i].to>>edge[i].dis;
    22. for(int i=1;i<=n;++i) //并查集初始化
    23. s[i]=i;
    24. sort(edge+1,edge+1+m,cmp);//对边做排序
    25. for(int i=1;i<=m;++i){ //贪心:逐一加入每个边
    26. if(find(edge[i].from)!=find(edge[i].to)){ //边的端点属于同一个集吗
    27. ans+=edge[i].dis; //计算MST
    28. union_set(edge[i].from,edge[i].to);//合并
    29. ++cnt; //小 统计MST中的点数
    30. }
    31. if(cnt==n-1) break;
    32. }
    33. if(cnt!=n-1) cout<<"-1";
    34. else cout<<ans;
    35. return 0;
    36. }

    python

    在python代码中判断有无最小生成树是通过并查集的find函数实现的。

    1. edges = []
    2. added_edges = []
    3. s = [] #并查集
    4. def find(x): #查询并查集,返回x的根
    5. if s[x] == x: return x
    6. s[x] = find(s[x]) #路径压缩
    7. return s[x]
    8. def main():
    9. n,m = map(int,input().split())
    10. for i in range(1, m + 1):
    11. x, y, z = map(int,input().split())
    12. edges.append((x, y, z)) #读边
    13. #下面是kruskal
    14. edges.sort(key=lambda tup: tup[2]) #对边做排序
    15. for i in range(n + 1): s.append(i) #并查集初始化
    16. for i in range(m):
    17. x, y = edges[i][0], edges[i][1]
    18. e1, e2 = find(x), find(y)
    19. if e1 == e2: #属于同一个集:产生了圈,丢弃
    20. continue
    21. else:
    22. added_edges.append(i)
    23. s[s[x]] =s[y] #合并
    24. find(1)
    25. for i in range(2, n + 1):
    26. if find(i) != s[i - 1]:
    27. print("-1")
    28. return
    29. ans = 0
    30. for i in added_edges:
    31. ans += edges[i][2]
    32. print(ans)
    33. return
    34. main()

  • 相关阅读:
    如何编写一个短线交易策略(收藏)
    go-zero微服务的使用
    JavaSE&Java8 时间日期API + 使用心得
    Mysql之聚合函数
    如何获取量化交易历史复权数据?
    Facebook公共主页受限需要提供哪些证件进行复审?
    C# - Enum各种转换
    婚恋交友系统源码-交友APP小程序H5开发-源码交付,支持二开-实名制交友更放心!
    计算机毕业设计之java+springboot基于vue的校园疫情防控系统
    AquilaChat2-34B 主观评测接近GPT3.5水平,最新版本Base和Chat权重已开源!
  • 原文地址:https://blog.csdn.net/qq_51118755/article/details/125014750