图的最小生成树—Kruskal算法
Kruskal算法的思想是:
选择最短的边
将边的起点和终点加入集合中,将边标记后选出
再次选择最短的边,且该边的起点和终点不在同一集合中 (避免产生回路)
再次将点纳入集合,将边选出
重复步骤3,4,直到将n-1条边选出到生成树(最小生成树只有n-1条边 )中。
Kruskal算法是对边进行遍历 ,只要每次选出来的边是合法的且最短的,生成树就是最小生成树
合法的:边的起点终点不在同一集合中,即不产生回路
最短的:满足前提条件下权值最小的
如何判断两点是否在同一集合中?:利用并查集
示例题目:
镖局运镖需要向路上的绿林好汉打点,每条路的打点费用有高有低,镖局如何选择道路,使得镖局可以到达任意城镇且打点费用最少?
数据给出格式如下:
1 2 3 4 5 6 7 8 9 10 6 9 2 4 11 3 5 13 4 6 3 5 6 4 2 3 6 4 5 7 1 2 1 3 4 9 1 3 2
第一行两个数字 a b ,表示有a个城镇,b条道路
接下来b行,每行三个数字 u v w ,表示从 u城到v城的打点费用为w
输出选择过程和最终结果
解题代码
整体代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <iostream> #include <map> #include <algorithm> using namespace std;multimap<int , pair<int , int >> roads; int city, road;int minleng = 0 ;int findFather (int * cityArray, int v) { if (cityArray[v] == v) { return v; } else { cityArray[v] = findFather (cityArray, cityArray[v]); return cityArray[v]; } } bool merge (int * cityArray,int v, int u) { int t1 = findFather (cityArray,v); int t2 = findFather (cityArray,u); if (t1 != t2) { cityArray[t2] = t1; return false ; } else { return true ; } } int main () { cin >> city >> road; int a, b, c; for (int i = 0 ; i < road; i++) { cin >> a >> b >> c; roads.insert (pair<int , pair<int , int >>(c, make_pair (a, b))); } int * cityArray = new int [city + 1 ]; for (int i = 1 ; i <= city; i++) { cityArray[i] = i; } int cityA, cityB, leng,counter=0 ; for (multimap<int , pair<int , int >>::iterator it=roads.begin ();it!=roads.end ();it++) { cityA = it->second.first; cityB = it->second.second; leng = it->first; if (merge (cityArray, cityA, cityB) == false ) { cout << "第" <<counter+1 <<"步选择了:" << cityA << " " << cityB << " " << leng << endl; minleng += leng; counter++; } if (counter == city - 1 ) { break ; } } cout << minleng; return 0 ; }
这里利用了map可以按key值从小到大排序,key值直接存储打点费用,value值则存储起点终点对组
利用并查集 判断两点是否在同一集合中
利用迭代器对map进行遍历,注意取值的first,second要用指针(即->)
Kruskal算法代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int cityA, cityB, leng,counter=0 ;for (multimap<int , pair<int , int >>::iterator it=roads.begin ();it!=roads.end ();it++){ cityA = it->second.first; cityB = it->second.second; leng = it->first; if (merge (cityArray, cityA, cityB) == false ) { cout << "第" <<counter+1 <<"步选择了:" << cityA << " " << cityB << " " << leng << endl; minleng += leng; counter++; } if (counter == city - 1 ) { break ; } }
输出结果:
1 2 3 4 5 6 第1步选择了:1 2 1 第2步选择了:1 3 2 第3步选择了:4 6 3 第4步选择了:5 6 4 第5步选择了:3 4 9 19