最小生成樹

最小生成樹:

在一個無向連通圖中,如果存在一個連通子圖包含原圖中所有結點和部分邊,且這個子圖不存在迴路,則這個子圖是原圖的一棵生成樹。在帶權圖中,所有生成樹中邊權最小的那棵或幾棵稱爲生成樹。

Kruskal算法求最小生成樹:

1,首先所有結點都屬於孤立的集合;

2,按照邊權從小到大的順序遍歷所有的邊,如果遍歷到的邊連接的兩個結點在不同的集合中,則合併這兩個集合;

3,當所有的邊遍歷完成時,如果只存在一個連通分量,則該連通分量就是我們要求的最小生成樹;如果不止存在一個連通分量,則說明原圖不連通,不存在最小生成樹。

可以看出Kruskal算法求解最小生成樹涉及很多的集合操作,所以可以將並查集運用其中。

九度1017:還是暢通工程 題目地址:http://ac.jobdu.com/problem.php?pid=1017

題目描述:

    某省調查鄉村交通狀況,得到的統計表中列出了任意兩村莊間的距離。省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可),並要求鋪設的公路總長度爲最小。請計算最小的公路總長度。
輸入:

    測試輸入包含若干測試用例。每個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。爲簡單起見,村莊從1到N編號。
    當N爲0時,輸入結束,該用例不被處理。

輸出:

    對每個測試用例,在1行裏輸出最小的公路總長度。

樣例輸入:
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

樣例輸出:

3
5

解題思路:根據題意(使全省任何兩個村莊間都可以實現公路交通,計算最小的公路總長度)可以看出是要求最小生成樹。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
 
#define N 101
int root[N];
 
struct Edge{
    int v1;//頂點1 
    int v2;//頂點2 
    int cost;//邊的權值 
     
    //重載操作符<,方便按邊的權值進行排序
    bool operator < (const Edge &A) const{
        return cost < A.cost;
    } 
};
 
int findroot(int x){
    if(root[x] == -1) return x;
    else{
        int tmp = findroot(root[x]);
        root[x] = tmp;
        return tmp;
    }
}
 
int main(){
    int n;
    while(cin >> n){
        if(n == 0) break;
         
        for(int i = 1;i <= n;i++) root[i] = -1;
         
        int e = n * (n - 1) / 2;
         
        vector<Edge> edge;
        for(int i = 0;i < e;i++){
            Edge e;
            cin >> e.v1 >> e.v2 >> e.cost;
            edge.push_back(e);
        }
        //按邊的權值進行排序 
        sort(edge.begin(),edge.end());
         
        int ans = 0;
        for(int i = 0;i < e;i++){
            int a = findroot(edge[i].v1);
            int b = findroot(edge[i].v2);
             
            if(a != b){
                root[a] = b;
                ans += edge[i].cost;
            }
        }
        cout << ans << endl;
    }
     
    return 0;
}
 
/**************************************************************
    Problem: 1017
    User: cherish
    Language: C++
    Result: Accepted
    Time:30 ms
    Memory:1712 kb
****************************************************************/

九度1144:Freckles 題目地址:http://ac.jobdu.com/problem.php?pid=1144
題目描述:

    In an episode of the Dick Van Dyke show, little Richie connects the freckles on his Dad's back to form a picture of the Liberty Bell. Alas, one of the freckles turns out to be a scar, so his Ripley's engagement falls through. 
    Consider Dick's back to be a plane with freckles at various (x,y) locations. Your job is to tell Richie how to connect the dots so as to minimize the amount of ink used. Richie connects the dots by drawing straight lines between pairs, possibly lifting the pen between lines. When Richie is done there must be a sequence of connected lines from any freckle to any other freckle. 

輸入:

    The first line contains 0 < n <= 100, the number of freckles on Dick's back. For each freckle, a line follows; each following line contains two real numbers indicating the (x,y) coordinates of the freckle.

輸出:

    Your program prints a single real number to two decimal places: the minimum total length of ink lines that can connect all the freckles.

樣例輸入:
3
1.0 1.0
2.0 2.0
2.0 4.0
樣例輸出:
3.41

解題思路:題目給出的是每個雀斑的座標,要求連接這些雀斑所需要的最少的墨水...(真無聊==)

中心思想也是以各個雀斑爲頂點求最小生成樹。但是給出的座標,所以還需要進行一下處理,計算每兩個頂點之間的距離,迴歸到最小生成樹正常情形。

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
 
#define N 101
int root[N];//存儲節點的父節點,用來供並查集使用 
 
struct Vertex{
    double x;
    double y;
};
 
struct Edge{
    int v1;
    int v2;
    double cost;
     
    bool operator <(const Edge &A) const{
        return cost < A.cost;
    }
};
 
//查找節點x所在樹的根結點 
int findroot(int x){
    if(root[x] == -1) return x;
    else{
        int tmp = findroot(root[x]);
        root[x] = tmp;//路徑壓縮 
        return tmp;
    }
}
  
int main(){
    int n;
    while(cin >> n){
        if(n == 0) break;
         
        for(int i = 0;i < n;i++) root[i] = -1;
         
        vector<Vertex> vertex_vec;//存儲節點 
        vector<Edge> edge_vec;//存儲邊
         
        for(int i = 0;i < n;i++){
            Vertex v;
            cin >> v.x >> v.y;
            vertex_vec.push_back(v);
        }
         
        //每兩個不同的節點組成一條邊 
        for(int i = 0;i < n;i++){
            for(int j = i + 1;j < n;j++){
                Edge e;
                e.v1 = i;
                e.v2 = j;
                double a = vertex_vec[i].x - vertex_vec[j].x;
                double b = vertex_vec[i].y - vertex_vec[j].y;
                e.cost = sqrt(a * a + b * b);
                edge_vec.push_back(e);
            }
        }
         
        //按邊的權值進行排序 
        sort(edge_vec.begin(),edge_vec.end());
         
          
        double ans = 0;
        for(int i = 0;i < edge_vec.size();i++){
             
            //查找該邊的兩個頂點的集合信息 
            int a = findroot(edge_vec[i].v1);
            int b = findroot(edge_vec[i].v2);
             
            //如果兩個頂點在不同的集合 
            if(a != b){
                root[a] = b;//合併兩個集合 
                ans += edge_vec[i].cost;//把該邊的權值加入 
            }
        }
         
        cout << fixed << setprecision(2) << ans << endl;
    }
     
    return 0;
} 
 
/**************************************************************
    Problem: 1144
    User: cherish
    Language: C++
    Result: Accepted
    Time:10 ms
    Memory:1780 kb
****************************************************************/

九度1154:Jungle Roads 題目地址:http://ac.jobdu.com/problem.php?pid=1154
題目描述:

   

    The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessly, so the large road network is too expensive to maintain. The Council of Elders must choose to stop maintaining some roads. The map above on the left shows all the roads in use now and the cost in aacms per month to maintain them. Of course there needs to be some way to get between all the villages on maintained roads, even if the route is not as short as before. The Chief Elder would like to tell the Council of Elders what would be the smallest amount they could spend in aacms per month to maintain roads that would connect all the villages. The villages are labeled A through I in the maps above. The map on the right shows the roads that could be maintained most cheaply, for 216 aacms per month. Your task is to write a program that will solve such problems.

輸入:

    The input consists of one to 100 data sets, followed by a final line containing only 0. Each data set starts with a line containing only a number n, which is the number of villages, 1 < n < 27, and the villages are labeled with the first n letters of the alphabet, capitalized. Each data set is completed with n-1 lines that start with village labels in alphabetical order. There is no line for the last village. Each line for a village starts with the village label followed by a number, k, of roads from this village to villages with labels later in the alphabet. If k is greater than 0, the line continues with data for each of the k roads. The data for each road is the village label for the other end of the road followed by the monthly maintenance cost in aacms for the road. Maintenance costs will be positive integers less than 100. All data fields in the row are separated by single blanks. The road network will always allow travel between all the villages. The network will never have more than 75 roads. No village will have more than 15 roads going to other villages (before or after in the alphabet). In the sample input below, the first data set goes with the map above.

輸出:

    The output is one integer per line for each data set: the minimum cost in aacms per month to maintain a road system that connect all the villages. Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit.

樣例輸入:
9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0
樣例輸出:
216
30
解題思路:也是基本的求最小生成樹的題目。只需注意一下輸入數據的形式和處理。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
 
int root[30];
 
struct Road{
    int village1;
    int village2;
    int distance;
     
    //重載<操作符,便於根據distance對road進行排序 
    bool operator < (const Road &R) const{
        return distance < R.distance;
    }   
};
 
//查找x節點所在的樹的根節點 
int findroot(int x){
    if(root[x] == -1) return x;
    else{
        int tmp = findroot(root[x]);
        root[x] = tmp;
        return tmp;
    }
}
 
int main(){
    int n;
    while(cin >> n){
        if(n == 0) break;
         
        vector<Road> road_vec;
         
        for(int i = 1;i <= n;i++) root[i] = -1;
         
        for(int i = 0; i < n - 1;i++){
            char c;
            int num; 
            cin >> c >> num;
             
            if(num >> 0){
                Road r;
                for(int j = 0;j < num;j++){
                    char tmpc;
                    int dis;
                    cin >> tmpc >> dis;
                     
                    //A表示1號village,B表示2號... 
                    r.village1 = c - 'A' + 1;
                    r.village2 = tmpc - 'A' + 1;
                    r.distance = dis;
                    road_vec.push_back(r);
                }
            }
        }
         
        sort(road_vec.begin(),road_vec.end());
         
        int ans = 0;
        for(int i = 0;i < road_vec.size();i++){
            int a = findroot(road_vec[i].village1);
            int b = findroot(road_vec[i].village2);
             
            if(a != b){
                root[a] = b;
                ans += road_vec[i].distance;
            }
        }
         
        cout << ans << endl;
    }
     
    return 0;
} 
 
/**************************************************************
    Problem: 1154
    User: cherish
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:1520 kb
****************************************************************/

九度1024:暢通工程 題目地址:http://ac.jobdu.com/problem.php?pid=1024
題目描述:
    省政府“暢通工程”的目標是使全省任何兩個村莊間都可以實現公路交通(但不一定有直接的公路相連,只要能間接通過公路可達即可)。經過調查評估,得到的統計表中列出了有可能建設公路的若干條道路的成本。現請你編寫程序,計算出全省暢通需要的最低成本。
輸入:
    測試輸入包含若干測試用例。每個測試用例的第1行給出評估的道路條數 N、村莊數目M (N, M < =100 );隨後的 N 行對應村莊間道路的成本,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間道路的成本(也是正整數)。爲簡單起見,村莊從1到M編號。當N爲0時,全部輸入結束,相應的結果不要輸出。
輸出:
    對每個測試用例,在1行裏輸出全省暢通需要的最低成本。若統計數據不足以保證暢通,則輸出“?”。
樣例輸入:
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
樣例輸出:
3
?
解題思路:也是基本的求解最小生成樹的問題。但是要注意的是該題與上面幾題不同的是,這題中不一定存在最小生成樹,該題中全省的所有村莊不一定全部連通。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
 
#define N 101
int root[N];
 
struct Road{
    int v1;//村莊1 
    int v2;//村莊2 
    int cost;//連接村莊1和村莊2的cost 
     
    bool operator < (const Road &R) const{
        return cost < R.cost;
    }
};
 
//查找x節點所在的樹的根節點 
int findroot(int x){
    if(root[x] == -1) return x;
    else{
        int tmp = findroot(root[x]);
        root[x] = tmp;//路徑壓縮 
        return tmp;
    }
}
 
int main(){
    int n,m;
    while(cin >> n){
        if(n == 0) break;
        cin >> m;
         
        vector<Road> road_vec;
         
        for(int i = 1;i <= m;i++) root[i] = -1;
         
        for(int i = 0;i < n;i++){
            Road r;
            cin >> r.v1 >> r.v2 >> r.cost;
            road_vec.push_back(r);
        }
         
        sort(road_vec.begin(),road_vec.end());
         
        int ans = 0;
        for(int i = 0;i < road_vec.size();i++){
            int a = findroot(road_vec[i].v1);
            int b = findroot(road_vec[i].v2);
             
            //如果道路road_vec[i]連接的兩個村莊不在同一個集合則進行合併 
            if(a != b){
                root[a] = b;//合併 
                ans += road_vec[i].cost;//把該道路的cost加入 
            }
        }
         
        //判斷整個省是否全部連通,如果所有村莊都在同一個集合,則所有村莊全部連通
        //cnt用來記錄連通分量的個數 
        int cnt = 0;
        for(int i = 1;i <= m;i++)
            if(root[i] == -1) cnt++;
        if(cnt > 1) cout << "?" << endl;
        else cout << ans << endl;
         
    }
     
    return 0;
} 
 
/**************************************************************
    Problem: 1024
    User: cherish
    Language: C++
    Result: Accepted
    Time:10 ms
    Memory:1520 kb
****************************************************************/

發佈了47 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章