对于一个边带有权值的图,我们可以求它的最小生成树(Minimum Spanning Tree)。通常有两种算法,Prim和Kruskal。
Prim算法的思路是每次找到距离当前生成树最近的一个结点,并将它连到当前的生成树。
代码如下:
1 //Prim 2 #include <iostream> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::vector; 10 11 const int INF = 0x3f3f3f3f; 12 13 struct Edge { 14 int vertex, weight; 15 }; 16 17 class Graph { 18 private: 19 int n; 20 bool *visited; 21 vector<Edge> *edges; 22 public: 23 //dist stores the min distance of a node to the tree 24 int *dist; 25 Graph (int input_n) { 26 n = input_n; 27 edges = new vector<Edge>[n]; 28 dist = new int[n]; 29 visited = new bool[n]; 30 memset(visited, false, n * sizeof(bool)); 31 memset(dist, 0x3f, n * sizeof(int)); 32 } 33 ~Graph() { 34 delete[] dist; 35 delete[] visited; 36 delete[] edges; 37 } 38 void insert(int x, int y, int weight) { 39 edges[x].push_back(Edge{y, weight}); 40 edges[y].push_back(Edge{x, weight}); 41 } 42 int prim(int v) { 43 int total_weight=0; 44 //v is the starting node 45 dist[v]=0; 46 //loop for n times, each time settle one node 47 for(int i=0;i<n;i++){ 48 int min_dist=INF,min_vertex; 49 for(int j=0;j<n;j++){ 50 //choose the node with min dist to the tree 51 if(!visited[j]&&dist[j]<min_dist){ 52 min_dist=dist[j]; 53 min_vertex=j; 54 } 55 } 56 total_weight+=min_dist; 57 visited[min_vertex]=1; 58 //update the nodes connected to the min_vertex 59 //dist will store the min dist to the tree 60 for(Edge &j: edges[min_vertex]){ 61 if(!visited[j.vertex]&&j.weight<dist[j.vertex]){ 62 dist[j.vertex]=j.weight; 63 } 64 } 65 } 66 return total_weight; 67 } 68 };
Kruskal的思路是每次找到剩余的权值最小的边,如果这条边与当前MST不构成环,则将这条边加入MST。利用并查集,我们可以很轻松地判断边是否会与MST构成环,因为如果边的两个顶点在并查集中属于同一棵树,那么这条边一定会形成环。
代码如下:
1 //kruskal 2 #include <iostream> 3 #include <algorithm> 4 #include <vector> 5 #include <set> 6 using namespace std; 7 8 //father of nodes 9 int *father; 10 struct Edge{ 11 int ver1,ver2,weight; 12 bool operator< (const Edge &e) const{ 13 return weight<=e.weight; 14 } 15 }; 16 vector<Edge> MST; 17 18 void initialize(int size){ 19 father=new int[size]; 20 for(int i=0;i<size;i++){ 21 father[i]=i; 22 } 23 } 24 25 int find_root(int node){ 26 if(father[node]!=node){ 27 father[node]=find_root(father[node]); 28 } 29 return father[node]; 30 } 31 32 bool merge(int node1, int node2){ 33 int ancestor1=find_root(node1); 34 int ancestor2=find_root(node2); 35 if(ancestor1!=ancestor2){ 36 father[ancestor2]=ancestor1; 37 return true; 38 } 39 return false; 40 } 41 42 int kruskal(int num_of_N, int num_of_E){ 43 int ans=0; 44 initialize(num_of_N); 45 //边按权值从小到大排序 46 set<Edge> edges; 47 int v1,v2,weight; 48 //输入 49 for(int i=0;i<num_of_E;i++){ 50 cin>>v1>>v2>>weight; 51 edges.insert({v1-1,v2-1,weight}); 52 } 53 set<Edge>::iterator iter=edges.begin(); 54 while(iter!=edges.end()){ 55 //merge成功说明这两个结点原本不在一棵树上,不构成环 56 //因此将这条边加入MST 57 if(merge(iter->ver1, iter->ver2)){ 58 MST.push_back(*iter); 59 ans+=iter->weight; 60 } 61 ++iter; 62 } 63 return ans; 64 } 65 66 int main(){ 67 int n,m; 68 cin>>n>>m; 69 int t=kruskal(n, m); 70 cout<<t<<endl; 71 vector<Edge>::iterator ite=MST.begin(); 72 while(ite!=MST.end()){ 73 cout<<ite->ver1<<" "<<ite->ver2<<" "<<ite->weight<<endl; 74 ite++; 75 } 76 delete[] father; 77 }