次小生成树学习:
顾名思义,次小生成树,就是将图的所有生成树排序后,权值第二小的生成树。
次小生成树的朴素求法是很好想的,即首先求出最小生成树,之后枚举最小生成树中的所有边,将当前枚举的边“禁止使用”,在这基础之上再求最小生成树,将所有边枚举之后的结果取最小值,那就是次小生成树。这个算法简单暴力,但是可想而知的复杂度是比较大的,在图是稠密图的时候,复杂度接近O(n^3)。在规模较大的时候不建议使用。
另一个推荐的求法:在添加最小生成树的边之外的边时,会形成环,这个时候,把在这个环中,且在最小生成树中的边给去掉,这时候就形成了另一个生成树。如何实现这个算法的呢?实际上,对于两个点u,v,我们遍历最小生成树,找出u到v的边中的最大权值的边,用一个数组maxn[u][v]保存起来。当我们往最小生成树中加边时,就直接用w - max[u][v] + 当前添加边的权值,这个式子计算出此时生成树的权值之和。那么maxn[u][v]这个数组是如何求得呢?嗯,bfs,即广度优先搜索,对每一个点都遍历一次生成树,每次更新的值都是当前点到其他点的权值。之后就枚举不在生成树的边进行计算,把每次的结果都保存下来,取最小的,就是次小生成树啦。(或许第k小生成树可以这么求?)这个算法的复杂度为O(n^2)。
例题:
https://vjudge.net/problem/POJ-1679
题意:
这题问的是最小生成树是否唯一。
思路:
那么很显然的,如果说最小生成树不唯一的话,那么最小生成树与次小生成树的权值肯定相等,因为对于在最小生成树中的边来说,至少存在一条不在其中的边,与在其中的边的权值相等,这样才满足不唯一,所以只需要求出次小生成树,判断其与最小生成树是否相等就可以了。
PS:通过这题还学会了用邻接链表表示图,用vector很方便的,就是把以每个点为起点的边搞到一起就ok。
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 8 struct node 9 { 10 int x,y,w; 11 } e[6000]; 12 13 struct ord 14 { 15 int p,dis; 16 }; 17 18 vector<node> v[105]; 19 20 int par[105]; 21 int maxn[105][105]; 22 23 bool used[6000],vis[6000]; 24 25 void init(int n) 26 { 27 for (int i = 1;i <= n;i++) 28 par[i] = i; 29 } 30 31 int fin(int x) 32 { 33 if (x == par[x]) return x; 34 else return par[x] = fin(par[x]); 35 } 36 37 void unit(int x,int y) 38 { 39 x = fin(x); 40 y = fin(y); 41 42 if (x != y) par[x] = y; 43 } 44 45 bool cmp(node aa,node bb) 46 { 47 return aa.w < bb.w; 48 } 49 50 void adde(int x,int y,int dis) 51 { 52 node t; 53 t.x = x; 54 t.y = y; 55 t.w = dis; 56 v[x].push_back(t); 57 } 58 59 void bfs(int k) 60 { 61 memset(used,0,sizeof(used)); 62 63 ord now,nex; 64 65 now.p = k;now.dis = 0; 66 used[k] = true; 67 68 queue<ord> qq; 69 70 while (!qq.empty()) qq.pop(); 71 72 qq.push(now); 73 74 while (!qq.empty()) 75 { 76 //printf("dsfsd "); 77 ord t = qq.front();qq.pop(); 78 79 int x = t.p,tmaxn = t.dis; 80 81 for (int i = 0;i < v[x].size();i++) 82 { 83 node tt = v[x][i]; 84 85 nex.dis = tt.w;nex.p = tt.y; 86 87 if (!used[nex.p]) 88 { 89 if (tmaxn > nex.dis) nex.dis = tmaxn; 90 used[nex.p] = true; 91 maxn[k][nex.p] = nex.dis; 92 qq.push(nex); 93 } 94 } 95 } 96 } 97 98 int main() 99 { 100 int t; 101 102 scanf("%d",&t); 103 104 while (t--) 105 { 106 int n,m; 107 108 scanf("%d%d",&n,&m); 109 110 init(n); 111 112 memset(v,0,sizeof(v)); 113 memset(vis,0,sizeof(vis)); 114 memset(used,0,sizeof(used)); 115 116 for (int i = 0;i < m;i++) 117 { 118 scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w); 119 } 120 121 sort(e,e+m,cmp); 122 123 int ans = 0; 124 125 for (int i = 0;i < m;i++) 126 { 127 int x = e[i].x,y = e[i].y; 128 129 if (fin(x) == fin(y)) continue; 130 131 unit(x,y); 132 133 vis[i] = true; 134 135 ans += e[i].w; 136 137 adde(x,y,e[i].w); 138 adde(y,x,e[i].w); 139 } 140 141 for (int i = 1;i <= n;i++) 142 { 143 bfs(i); 144 } 145 146 int minn = 100000000; 147 148 for (int i = 0;i < m;i++) 149 { 150 if (!vis[i]) 151 { 152 int tans = ans; 153 int x = e[i].x,y = e[i].y; 154 155 tans -= maxn[x][y]; 156 157 tans += e[i].w; 158 159 if (tans < minn) minn = tans; 160 } 161 162 163 } 164 165 if (minn == ans) printf("Not Unique! "); 166 else printf("%d ",ans); 167 } 168 169 return 0; 170 }