贪心算法训练(三)——最小生成树

  • 问题描述
    • 求一个连通无向图的最小生成树的代价(图边权值为正整数)
  • 输入
    • 第一行是一个整数 N (1 <= N <= 20) ,表示有多少个图需要计算,以下有 N 个图,第 i 图的第一行是一个整数 M (1 <= M <= 50) ,表示图的顶点数,第 i 图的第 2 行至 1+M 行为一个 M*M 的二维矩阵,其元素 ai,j 表示图的 i 顶点和 j 顶点的连接情况,如果 ai ,j = 0,表示 i 顶点和 j 顶点不相连,如果 ai ,j > 0,表示 i 顶点和 j 顶点的连接权值
  • 输出
    • 每个用例,用一行输出对应图的最小生成树的代价
  • 样例输入
      1
      6
      0 6 1 5 0 0
      6 0 5 0 3 0 
      1 5 0 5 6 4
      5 0 5 0 0 2
      0 3 6 0 0 6
      0 0 4 2 6 0
  • 样例输出
    15
  • Kruskal 算法
    • 对于一个 N*N 矩阵,先构造 N 个没有连接的顶点,按边权值大小从小到大选择顶点,连接不同的生成树,一条边连接 2 个顶点,那么这两个顶点就在同一颗树上,要保证每次连接的树都是不同的,直到只剩下一颗树为止,假如现有一条边连接了 A,B 两个顶点,另一条连接了 B,C 顶点,那么 A,B,C在同一颗树上, A,C 不能再被边连接,同样, C 顶点连接的其他顶点也不能和 A 相连,那么如何做出这样的判断呢?
  • 算法设计
    • 对边权值从小到大排序,采用链式结构,每个节点存储边连接的左右端点
    • 判断边的加入是否构成了环,如果构成了环,那么这条边的加入是不合理的
  • 代码
    #include<iostream>
    
    using namespace std;
    
    struct Node
    {
        int l;
        int r;
        int len;
        Node *next;
    };
    
    void insert(Node *&head,Node *p);
    
    int main()
    {
        Node *head,*p;
        int n,m,x,temp;
        int *a;
        int i,j;
        int sum;
        cin>>n;
        while(n--)
        {
            sum = 0;
            cin>>m;
            a = new int[m+1];
            for (i = 1; i <= m; i++)
            {
                a[i] = i;               //各端点自为一组
            }
            head = new Node;
            p = head;
            p->next = NULL;
            for (i = 1; i <= m; i++)
                for (j = 1; j <= m; j++)
                {
                    cin>>x;
                    if (i > j && x != 0)   //对称矩阵
                    {
                        p=new Node;
                        p->l = i;
                        p->r = j;
                        p->len = x;
                        p->next = NULL;
                        insert(head,p);
                    }
                }
            p = head->next;
            while (p)
            {
                if (a[p->l] != a[p->r])  //判断是否为同一组
                {
                    sum += p->len;
                    temp = a[p->l];       //记录左端点
                    for(i = 1; i <= m; i++)
                        if (a[i] == temp)   //找到原左端点的值
                        {
                            a[i] = a[p->r]; //将左端点的值改为和右端点一致
                        }
                }
                p = p->next;
            }
            cout<<sum<<endl;
        }
        return 0;
    }
    
    void insert(Node *&head,Node *p)
    {
        Node *q = head;
        while(q->next && q->next->len <= p->len)  //从头结点开始,大的放后面
        {
            q = q->next;
        }
        p->next = q->next;
        q->next = p;
    }
posted @ 2018-08-11 09:14  Nikki_o3o  阅读(409)  评论(0编辑  收藏  举报