图论 最小生成树问题

最小生成树问题可以算是最短路径的升级版;

 

先注意一下两者的区别:

最短路径针对的是单源最短问题;

最小生成树针对的是全局最短问题,最短路径貌似可以解决最小生成树问题,但是并不能保证全局路径最短;

 

针对于最小生成树问题,总的来说有两种解决办法: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;
}

  

 

posted @ 2020-03-28 20:55  暮云林凌  阅读(390)  评论(0)    收藏  举报