经典的最小生成树例子,Prime算法,具体的步骤及其注释本人均在代码中附加,请仔细阅读与品味,要求,可以熟练的打出。
1 //Prime算法基础 2 #include3 using namespace std; 4 5 int main() 6 { 7 int n,m,i,j,k,min,t1,t2,t3; 8 int e[7][7],dis[7],book[7] = {0}; 9 int inf = 99999999; 10 int count = 0,sum = 0; 11 cin >> n >> m; 12 13 //初始化 用邻接矩阵存储 14 for(int i = 1;i <= n;i++) 15 for(int j = 1;j <= n;j++) 16 if(i == j) 17 e[i][j] = 0; 18 else 19 e[i][j] = inf; 20 21 //读入边 无向图来回都得设置权值 22 for(int i = 1;i <= m;i++) 23 { 24 cin >> t1 >> t2 >> t3; 25 e[t1][t2] = t3; 26 e[t2][t1] = t3; 27 } 28 29 //初始化dis数组,这里是第一个顶点到各个顶点的初始距离,因为当前生成树中只有一个顶点 30 for(int i = 1;i <= n;i++) 31 dis[i] = e[1][i]; 32 33 //Prime核心部分 34 //将第一个顶点(即1号顶点)加入生成树 35 book[1] = 1;//这里用book数组来标记一个顶点是否已经加入生成树 36 count++;//表示生成树中已加入一个顶点 37 while(count < n) 38 { 39 min = inf; 40 41 //扫描找出距离当前根顶点相连接的最小权值的顶点 42 for(int i = 1;i <= n;i++) 43 { 44 if(book[i] == 0 && min > dis[i])//如果根节点未被标记并且根节点到剩下的n-1个顶点之间有直连线(就是相连着的边) 45 { 46 min = dis[i];//就将这条边的权值代替之前初始化时的无穷大 ,更新min的值,min的值更新只在此for循环中有效,此for循环目的是找出连接根节点权值最小的那个顶点j,再把min放入for循环中,一直比较,直至找到最小的权值及连的顶点j 47 j = i;//同时将此时的顶点的编号记录在临时变量j中,以便接下来使用 48 } 49 } 50 book[j] = 1;//将上面for循环中找到的最小权值的顶点j加入到生成树中并标记 51 count++;//生成树中的顶点加一 52 sum += dis[j];//将权值最小的边都累加,最终得到最优解 53 54 //扫描当前顶点j所有的边,再以j为中心点,更新生成树到每一个非树顶点的距离 55 for(k = 1;k <= n;k++) 56 { 57 if(book[k] == 0 && dis[k] > e[j][k])//e[k]表示第一个加入的根顶点到顶点k之间的权值(可能直接连接也可能间接连接),e[j][k]表示的是当前根顶点作为j,到与j顶点直接相连的顶点k的权值 58 dis[k] = e[j][k];//此循环的目的是找出离j顶点最近的顶点 59 } 60 } 61 cout << sum; 62 return 0; 63 } 64 /* 65 6 9 66 2 4 11 67 3 5 13 68 4 6 3 69 5 6 4 70 2 3 6 71 4 5 7 72 1 2 1 73 3 4 9 74 1 3 2 75 19 76 */
Kruskal算法,需要用到并查集,具体请看以下代码
1 //Kruskal算法 2 #include3 #include 4 using namespace std; 5 struct edge 6 { 7 int u; 8 int v; 9 int w; 10 }e[10];//为了方便排序,这里创建了一个结构体数组来存储边的关系,数组大小根据实际情况来设置,要比m的最大值大1 11 int n,m; 12 int f[7],sum = 0,countt = 0;//并查集需要用到的一些变量,f数组大小根据实际情况来设置,要比n的最大值大1 13 14 ////这个也是快速排序,也可以替代sort函数 15 //void quicksort(int left,int right) 16 //{ 17 // int i,j; 18 // struct edge t; 19 // if(left > right) 20 // return; 21 // i = left; 22 // j = right; 23 // while(i != j) 24 // { 25 // //顺序很重要,要先从右边开始找 26 // while(e[j].w >= e[left].w && i < j) 27 // j--; 28 // //再从左边开始找 29 // while(e[i].w <= e[left].w && i < j) 30 // i++; 31 // //交换 32 // if(i < j) 33 // { 34 // t = e[i]; 35 // e[i] = e[j]; 36 // e[j] = t; 37 // } 38 // } 39 // //最终将基准数归位,将left和i互换 40 // t = e[left]; 41 // e[left] = e[i]; 42 // e[i] = t; 43 // 44 // quicksort(left,i - 1);//继续处理左边的,这里是一个递归的过程 45 // quicksort(i + 1,right);//继续处理右边的,这里是一个递归的过程 46 // return; 47 //} 48 49 //并查集寻找祖先的函数 50 int getf(int v) 51 { 52 if(f[v] == v) 53 return v; 54 else 55 { 56 //这里是路径压缩 57 f[v] = getf(f[v]); 58 return f[v]; 59 } 60 } 61 //并查集合并两个子集合的函数 62 int merge(int v,int u) 63 { 64 int t1,t2; 65 t1 = getf(v); 66 t2 = getf(u); 67 if(t1 != t2)//判断两个点是否在同一个集合中 68 { 69 f[t2] = t1; 70 return 1; 71 } 72 return 0; 73 } 74 75 bool cmp(const edge &a,const edge &b) 76 { 77 return a.w < b.w; 78 } 79 80 int main() 81 { 82 //读入顶点个数n和边的条数m 83 cin >> n >> m; 84 //读入边,这里用结构体数组来存储边的关系 85 for(int i = 1;i <= m;i++) 86 cin >> e[i].u >> e[i].v >> e[i].w; 87 // quicksort(1,m);//按照权值从大到小对边进行快速排序 88 //快速排序权值 89 sort(e + 1,e + m + 1,cmp); 90 //并查集初始化 91 for(int i = 1;i <= n;i++) 92 f[i] = i; 93 94 //Kruskal算法核心部分 95 for(int i = 1;i <= m;i++) 96 { 97 //判断一条边的两个顶点是否已经连通,即判断是否已在同一个集合中 98 if(merge(e[i].u,e[i].v))//如果目前不连通,则选用这条边 99 { 100 countt++;//满足条件将顶点加入生成树 101 sum += e[i].w;//路径累加和 102 } 103 if(countt == n - 1)//当顶点均加入到生成树中时,结束循环 104 break; 105 } 106 cout << sum << endl; 107 return 0; 108 }
并查集例题
下面是对并查集使用的代码,并查集不太理解的可以看看下面的代码
1 //并查集的使用 2 #include3 using namespace std; 4 int f[1000],n,m,k,sum; 5 6 //这是找爹的递归函数,不停的去找爹,直到找到祖宗为止,即找到根节点,这个函数的目的是判断两个顶点是否属于同一个集合 7 int getf(int v) 8 { 9 if(f[v] == v) 10 return v; 11 else 12 { 13 /*这里是路径压缩,每次在函数返回的时候,顺带把同一个集合里面的元素都改为根节点的祖先编号,这样可以提高今后找到树的祖先的速度*/ 14 f[v] = getf(f[v]); 15 return f[v]; 16 } 17 } 18 //这里是合并两子集合的函数,在合并函数中调用getf函数,看两个顶点是否是属于同一个集合,然后再决定是否执行合并操作 19 void merge(int v,int u) 20 { 21 int t1,t2; 22 t1 = getf(v); 23 t2 = getf(u); 24 if(t1 != t2)//根据调用getf的函数,判断两个顶点是否在同一个集合中,即是否为同一个祖先 25 { 26 f[t2] = t1; 27 } 28 } 29 30 int main() 31 { 32 int x,y; 33 cin >> n >> m; 34 //这里是初始化,非常的重要,数组里面存的是自己数组下标的编号就好了 35 for(int i = 1;i <= n;i++) 36 f[i] = i; 37 for(int i = 1;i <= m;i++) 38 { 39 cin >> x >> y; 40 merge(x,y);//将两个顶点传入合并集合函数中进行操作 41 } 42 for(int i = 1;i <= n;i++) 43 { 44 if(f[i] == i)//根节点的值依旧是原先的值,即判断有几个顶点的值不变即有几个不同的集合 45 sum++; 46 } 47 cout << sum << endl; 48 return 0; 49 } 50 /* 51 10 9 52 1 2 53 3 4 54 5 2 55 4 6 56 2 6 57 8 7 58 9 7 59 1 6 60 2 4 61 3 62 */
✌