zoukankan      html  css  js  c++  java
  • 最小生成树(prim和Kruskal)

    还是畅通工程

    Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 70492    Accepted Submission(s): 31905


    Problem Description
    某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
     
    Input
    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
    当N为0时,输入结束,该用例不被处理。
     
    Output
    对每个测试用例,在1行里输出最小的公路总长度。
     
    Sample Input
    3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
     
    Sample Output
    3 5
    Hint
    Hint
    Huge input, scanf is recommended.
     
    Source
     
    此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
    1. 把图中的所有边按代价从小到大排序; 
    2. 把图中的n个顶点看成独立的n棵树组成的森林; 
    3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
    4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    #include <algorithm>
    #include <iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <vector>
    #include <map>
    #define ME(x , y) memset(x , y , sizeof(x))
    #define SF(n) scanf("%d" , &n)
    #define rep(i , n) for(int i = 0 ; i < n ; i ++)
    #define INF  0x3f3f3f3f
    #define mod 1024
    using namespace std;
    typedef long long ll ;
    int fa[10009];
    
    struct node{
        int from , to , va ;
    }a[10009];
    
    bool cmp(node a , node b)
    {
        return a.va < b.va ;
    }
    
    int find(int x)
    {
        return x = (fa[x] == x)? x : find(fa[x]);
    }
    
    void unite(int x , int y)
    {
        x = find(x) , y = find(y);
        if(x > y) fa[x] = y ;
        else
            fa[y] = x ;
    }
    
    int main()
    {
        int n ;
        while(~scanf("%d" , &n) && n)
        {
            int u , v , w  , dis = 0 , ans = 0 ;
            int num = n * (n - 1) / 2;
            for(int i = 1 ; i <= n ; i++) fa[i] = i ;
            for(int i = 0 ; i < num ; i++)
            {
                scanf("%d%d%d" , &u , &v , &w);
                a[i].from = u , a[i].to = v , a[i].va = w ;
            }
            sort(a , a + num , cmp);
            for(int i = 0 ; i < num ; i++)
            {
                if(find(fa[a[i].from]) != find(fa[a[i].to]))
                {
                    unite(a[i].from , a[i].to);
                    dis += a[i].va ;
                    ans ++ ;
                }
                if(ans == n - 1)
                {
                    break ;
                }
            }
            printf("%d
    " , dis);
        }
    
    
    
        return 0 ;
    }
    1. }closedge[vexCounts]

    这里写图片描述

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    #include <algorithm>
    #include <iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include <stdio.h>
    #include <queue>
    #include <string.h>
    #include <vector>
    #include <map>
    #define ME(x , y) memset(x , y , sizeof(x))
    #define SF(n) scanf("%d" , &n)
    #define rep(i , n) for(int i = 0 ; i < n ; i ++)
    #define INF  0x3f3f3f3f
    #define mod 1024
    using namespace std;
    typedef long long ll ;
    int map1[109][109];//记录所有路径的距离
    int dis[10009];//记录u集合到v的最小距离
    int n ;
    int vis[10009];
    int ans ;
    void prim()
    {
        for(int i = 1 ; i <= n ; i++)
        {
            vis[i] = 0 ;
            dis[i] = map1[1][i] ;//把1顶点放入u集合中,并记录该点到其他所有定点的距离
        }
        vis[1] = 1 ;//标记1加入u集合点
        for(int i = 2 ; i <= n ; i++)//要进行n-1次操作,也就是选出n-1条边
        {
            int min = INF;
            int pos ;
            for(int j = 1 ; j <= n ; j++)
            {
                if(!vis[j] && min > dis[j])//选出u集合各点到v集合各点中最小的权值边
                {
                    min = dis[j];
                    pos = j ;//记录最小权值边的的点
                }
            }
            vis[pos] = 1 ;//将该点放入u集合中,并标记
            ans += min ;//计算最小生成树
            for(int j = 1 ; j <= n ; j++)//u集合加入了新的点后,需要更新u集合各点到v集合各点的最小权值
            {
                if(!vis[j] && map1[pos][j] < dis[j])
                {
                    dis[j] = map1[pos][j];
                }
            }
        }
    }
    
    void init()
    {
        memset(map1 , INF , sizeof(map1));
        memset(vis , 0 , sizeof(vis));
        ans = 0 ;
    }
    
    int main()
    {
        while(~scanf("%d" , &n) && n)
        {
            init();
            int num = n * (n - 1) / 2 ;
            int u , v , w ;
            for(int i = 0 ; i < num ; i++)
            {
                scanf("%d%d%d" , &u , &v , &w);
                if(map1[u][v] > w)
                    map1[u][v] = map1[v][u] = w ;
            }
            prim();
            printf("%d
    " , ans);
        }
    
    
    
        return 0 ;
    }
  • 相关阅读:
    重构代码
    我的菜单在母版页,如何更改菜单点击后的效果
    ASP.NET网页显示LED字体
    点击一次铵钮产生一个新文本框,分别输入值,然后获取
    linux不同信号之间发送信号测试
    UCOSII移植ARM的笔记
    C语言 return返回值的作用
    C语言 return没有返回值.
    linux c语言链表的简单应用之创建链表
    sizeof与strlen的用法
  • 原文地址:https://www.cnblogs.com/nonames/p/11364572.html
Copyright © 2011-2022 走看看