貪心算法訓練(三)——最小生成樹

貪心算法訓練(三)——最小生成樹

  • 問題描述
    • 求一個連通無向圖的最小生成樹的代價(圖邊權值爲正整數)
  • 輸入
    • 第一行是一個整數 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 閱讀(...) 評論(...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章