图论 最小生成树问题
最小生成树问题可以算是最短路径的升级版;
先注意一下两者的区别:
最短路径针对的是单源最短问题;
最小生成树针对的是全局最短问题,最短路径貌似可以解决最小生成树问题,但是并不能保证全局路径最短;
针对于最小生成树问题,总的来说有两种解决办法:prim算法和Kruskal算法;
对于这两种算法,针对的场景不同。前者往往针对于稠密图,而后者往往针对于稀疏图;
Prim算法:
Prim算法总体思想是通过新加入的中间结点,借此来更新其他点到达该点集的距离,并且每次迭代选择距离点集最短路径的点;
其总体思想和迪杰斯特拉类似,还是每次选择最小距离进行更新,没啥可赘述的;
总体代码如下所示:
int prim() {
fill(d, d + maxn, INF);
d[0] = 0;
int ans = 0;
for (int i = 0; i < n; i++) {
int u = -1;
int Min = INF;
for (int j = 0; j < n; j++) {
if (!vis[j] && d[j] < Min) {
u = j;
Min = d[j];
}
}
if (u == -1)
return -1;
vis[u] = true;
ans += d[u];
for (int v = 0; v < n; v++) {
if (vis[u] == false && G[u][v] != INF && G[u][v] < d[v]) {
d[v] = G[u][v];
}
}
}
return ans;
}
其中值得注意的是distance的条件判断语句:
之前对于迪杰斯特拉来说,是以该节点作为中介点更新dis的整体距离;
但是对于Prim算法来说,dis并不代表源点到各点距离,而是代表点集到各点距离,所以自然判断的是新得到的可达路径是否可以代替点集到各点距离;
可以看到的是,由于该算法是针对于可达点来进行计算,所以最好针对稠密图;
Kruskal算法:
Prim算法针对的是点,而Kruskal算法针对的则是边;
对于该算法来说,主要是利用挑选最小边的贪心思路来进行抉择;
挑选标准:每次挑选属于不同两个集合的最小边长,以此保证每次挑选的边不会两节点同处一个子集,从而形成环;
大致流程:
1.进行边的从小到大排序;
2.每次挑选边权值最小,且两个结点同属不同集合的边,选做生成树路径之后,两边结点归并为一个集合;
const int maxv = 110;
const int maxe = 10010;
struct edge {
int u, v;
int cost;
}E[maxe];
bool cmp(edge a, edge b) {
return a.cost < b.cost;
}
int father[maxv];
int findFather(int x) {
int a = x;
while (x != father[x]) {
x = father[x];
}
while (a != father[a]) {
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
int kruskal(int n, int m) {
int ans = 0, Num_Edge = 0;
for (int i = 0; i < n; i++) {
father[i] = i;
}
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int fau = findFather(E[i].u);
int fav = findFather(E[i].v);
if (fau != fav) {
father[fau] = fav;
ans += E[i].cost;
Num_Edge++;
if (Num_Edge == n - 1)
break;
}
}
if (Num_Edge != n - 1)
return -1;
else
return ans;
}

浙公网安备 33010602011771号