第八章 其他算法(图的最小生成树、图的割点、图的割边、二分图最大匹配)
一、图的最小生成树
找出一个有权重连通图中的最小生成树,即:具有最小权重且连接到所有结点的树。(强调的是树,树是没有回路的)。
镖局运镖
已知城镇地图如下,顶点是城镇编号,边上的值表示这条道路上打点绿林好汉需要的银子数。
目的:镖局可以到达任意一个城镇,要求花费的银子越少越好。
实质:使用最少的边使图连通,且花费最少
输入:
第一行n,m(n个城市,m条道路)
接下来m行,输入a,b,c,城市a到城市b需要花费c(a,b间无向图)
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
输出:
19
Kruskal算法思想:
首先按照边的权值进行从小到大的排序,每次从剩余的边中选择权值较小且边的两个顶点不在同一个集合内的边(就是不会产生回路的边),
加入到生成树中,直到加入了n-1条边为止
1 #include <stdio.h> 2 int n, m,f[100]; 3 struct edge 4 { 5 int u; 6 int v; 7 int w; 8 }; 9 struct edge e[20]; 10 void quicksort(int left,int right) 11 { 12 int i,j; 13 struct edge t; 14 if (left >= right) 15 return; 16 i = left; 17 j = right; 18 while (i < j) 19 { 20 while (e[j].w >= e[left].w&&i<j) 21 j--; 22 while (e[i].w <= e[left].w&&i<j) 23 i++; 24 if (i < j) 25 { 26 t = e[i]; 27 e[i] = e[j]; 28 e[j] = t; 29 } 30 } 31 t = e[left]; 32 e[left] = e[i]; 33 e[i] = t; 34 quicksort(left,i-1); 35 quicksort(i+1,right); 36 return; 37 } 38 int getf(int t) 39 { 40 if (t == f[t]) 41 return t; 42 else 43 { 44 f[t]=getf(f[t]);//找到父节点 45 return f[t]; 46 } 47 } 48 int merge(int x,int y)//判断该边加入后,是否发生图联通 49 { 50 //只要x和y不在同一个集合之中,就是不联通 51 int t1, t2; 52 t1 = getf(x); 53 t2 = getf(y); 54 if (t1 == t2) 55 return 0; 56 else 57 { 58 f[t2] = f[t1]; 59 return 1; 60 } 61 } 62 int main() 63 { 64 int i,total=0,count=0; 65 scanf("%d%d",&n,&m); 66 for(i=1;i<=m;i++) 67 { 68 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 69 } 70 for (i = 1;i <= n;i++) 71 f[i] = i; 72 quicksort(1,m); 73 for (i = 1;i <= m;i++) 74 { 75 if (merge(e[i].u, e[i].v))//判断是否能加入成功 76 { 77 total += e[i].w; 78 count++; 79 } 80 if (count == n - 1) 81 break; 82 } 83 printf("%d\n",total); 84 return 0; 85 }
Prim算法
首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出与最小生成树中各结点权重最小边,并加入到最小生成树中。加入之后如果产生回路则跳过这条边,选择下一个结点。当所有结点都加入到最小生成树中之后,就找出了连通图中的最小生成树了。
1 #include <stdio.h> 2 #define Max 1000 3 int main() 4 { 5 int n, m, dis[10], e[10][10], book[10] = {0}; 6 int i,j,x,y,t; 7 int count = 0,sum=0,min,minx; 8 scanf("%d%d",&n,&m); 9 for (i = 1;i <= n;i++) 10 { 11 for (j = 1;j <= n;j++) 12 { 13 e[i][j] = Max; 14 if (i == j) 15 e[i][j] = 0; 16 } 17 } 18 for (i = 1;i <= m;i++) 19 { 20 scanf("%d%d%d",&x,&y,&t); 21 e[x][y] = t; 22 e[y][x] = t; 23 } 24 // 25 for (i = 1;i <= n;i++) 26 dis[i] = e[1][i]; 27 book[1] = 1; 28 count++; 29 while (count < n) 30 { 31 //找到最小值 32 min = Max; 33 for (i = 1;i <= n;i++) 34 { 35 if (book[i] == 0 && min > dis[i]) 36 { 37 min = dis[i]; 38 minx = i; 39 } 40 } 41 sum += min; 42 book[minx] = 1; 43 count++; 44 //加入顶点后更新 45 for (i = 1;i <= n;i++) 46 { 47 if (book[i] == 0 && dis[i] > e[minx][i]) 48 dis[i] = e[minx][i]; 49 } 50 } 51 printf("%d\n",sum); 52 return 0; 53 }
优化:使用邻接表和最小堆实现
1 #include <stdio.h> 2 #define Max 1000 3 int book[10], dis[10]; 4 int h[10], pos[10],size; 5 void swap(int x,int y) 6 { 7 int t; 8 t = h[x]; 9 h[x] = h[y]; 10 h[y] = t; 11 12 t = pos[h[x]]; 13 pos[h[x]] = pos[h[y]]; 14 pos[h[y]] = t; 15 } 16 void siftdown(int i) 17 { 18 int t, flag = 0; 19 while (2 * i <= size&&flag == 0) 20 { 21 t = i; 22 if (dis[h[i]] > dis[h[2 * i]]) 23 { 24 t = 2 * i; 25 } 26 if (2 * i + 1 <= size&&dis[h[2 * i + 1]] < dis[h[t]]) 27 t = 2 * i + 1; 28 if (t != i) 29 { 30 swap(t, i); 31 i = t; 32 } 33 else 34 flag = 1; 35 } 36 } 37 int pop() 38 { 39 int t; 40 t = h[1]; 41 pos[t] = 0; 42 h[1] = h[size]; 43 pos[h[1]] = 1; 44 size--; 45 siftdown(1); 46 return t;//dis中的下标 47 } 48 void siftup(int i) 49 { 50 int flag = 0; 51 if (i == 1) 52 return; 53 while (i != 1 && flag == 0) 54 { 55 if (dis[h[i]] < dis[h[i / 2]]) 56 swap(i, i / 2); 57 else 58 flag = 1; 59 i = i / 2; 60 } 61 62 } 63 int main() 64 { 65 int n, m, u[20], v[20], w[20]; 66 int i, j, k, count = 0, sum = 0, first[10], next[30]; 67 scanf("%d%d",&n,&m); 68 for (i = 1;i <= m;i++) 69 scanf("%d%d%d",u+i,v+i,w+i); 70 for (i = m + 1;i <= 2 * m;i++) 71 { 72 v[i] = u[i - m]; 73 u[i] = v[i - m]; 74 w[i] = w[i - m]; 75 } 76 for (i = 1;i <= n;i++) 77 first[i] = -1; 78 for (i = 1;i <= 2 * m;i++) 79 { 80 next[i] = first[u[i]]; 81 first[u[i]] = i; 82 } 83 //prim算法开始 84 //开始节点为1 85 book[1] = 1; 86 count++; 87 for (i = 1;i <= n;i++) 88 dis[i] = Max; 89 dis[1] = 0; 90 k = first[1]; 91 while (k != -1) 92 { 93 dis[v[k]] = w[k]; 94 k = next[k]; 95 } 96 //初始化堆,对dis进行处理 97 size = n; 98 for (i = 1;i <= size;i++) 99 { 100 h[i] = i; 101 pos[i] = i; 102 } 103 for (i = size / 2;i >= 1;i--) 104 { 105 siftdown(i); 106 } 107 pop(); 108 while (count < n) 109 { 110 j = pop(); 111 book[j] = 1; 112 count++; 113 sum += dis[j]; 114 k = first[j]; 115 while (k != -1) 116 { 117 if (book[v[k]] == 0 && dis[v[k]] > w[k]) 118 { 119 dis[v[k]] = w[k]; 120 siftup(pos[v[k]]); 121 } 122 k = next[k]; 123 } 124 } 125 printf("%d\n",sum); 126 return 0; 127 }
二、图的割点(重要城市)
输入
6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6
输出
2
1 #include <stdio.h> 2 int n, m, map[10][10]; 3 int num[10], low[10], flag[10], index, root; 4 int min(int a,int b) 5 { 6 return a < b ? a : b; 7 } 8 void dfs(int cur,int father) 9 { 10 int child = 0, i, j; 11 index++; 12 num[cur] = index; 13 low[cur] = index; 14 for (i = 1;i <= n;i++) 15 { 16 if (map[cur][i] == 1) 17 { 18 if (num[i] == 0) 19 { 20 child++; 21 dfs(i,cur); 22 low[cur] = min(low[cur], low[i]); 23 if (cur != root&&low[i] >= num[cur]) 24 flag[cur] = 1; 25 if (cur == root&&child == 2) 26 flag[cur] = 1; 27 } 28 else if(i!=father) 29 { 30 low[cur] = min(low[cur],num[i]); 31 } 32 } 33 } 34 } 35 int main() 36 { 37 int i, j,x,y; 38 scanf("%d%d",&n,&m); 39 for (i = 1;i <= m;i++) 40 { 41 scanf("%d%d",&x,&y); 42 map[x][y] = 1; 43 map[y][x] = 1; 44 } 45 root = 1; 46 dfs(1,root); 47 for (i = 1;i <= n;i++) 48 { 49 if (flag[i]) 50 printf("%d ",i); 51 } 52 printf("\n"); 53 return 0; 54 }
三、图的割边(关键道路)
四、二分图最大匹配
小可可今天和小伙伴们一起去游乐场玩,终于可以坐上梦寐以求的过山车了。过山车的每一排只有两个座位,为了安全起见,是每个女生必须与一个男生做一排。但是,每个人都希望与自己认识的人坐在一起。举个例子:1号女生与1号男生相互认识,因此1号女生和1号男生可以坐在一起。另外1号女生与2号男生也相互认识,因此他们也可以做一起。像这样的关系还有2号女生认识2号和3号男生,3号女生认识1号男生。请问如何安排座位才能让最多的人满意呢?这仅仅是一个例子。实际情况复杂得多,因为小可可的小伙伴们太多了。
输入:注:1、2、3为女生,4、5、6为男生
6 5
1 4
1 5
2 5
2 6
3 4
输出:
3
1 #include <stdio.h> 2 int map[20][20], match[20], book[20], n, m; 3 int dfs(int u) 4 { 5 int i; 6 for (i = 1;i <= n;i++) 7 { 8 if (book[i] == 0 && map[u][i] == 1) 9 { 10 book[i] = 1; 11 if (match[i] == 0 || dfs(match[i])) 12 { 13 match[i] = u; 14 match[u] = i; 15 return 1; 16 } 17 } 18 } 19 return 0; 20 } 21 int main() 22 { 23 int i, j, x, y, sum = 0; 24 scanf("%d%d", &n, &m); 25 for (i = 1;i <= m;i++) 26 { 27 scanf("%d%d", &x, &y); 28 map[x][y] = 1; 29 map[y][x] = 1; 30 } 31 for (i = 1;i <= n;i++) 32 { 33 for (j = 1;j <= n;j++) 34 book[j] = 0; 35 if (dfs(i)) 36 sum++; 37 } 38 printf("%d\n",sum); 39 return 0; 40 }

浙公网安备 33010602011771号