最小生成树算法

白书写到最小生成树了,突然发现自己忘得差不多,简单回忆一下

最小生成树问题

给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成树(Spanning Tree)如果边上有权值,那么使得边权和最小的生成树叫做最小生成树( MST, Minimum Spanning Tree )
最常见的问题,多个村子修路,使得道路建设费用最小。注意如果图不连通,肯定不存在生成树。

Prim 算法-给定初始点的最小生成树

我们假设有一棵只包含一个顶点 v v v的树 T T T,然后贪心地选取 T T T和其他顶点之间相连的最小权值的边,并把它加到 T T T中。不断进行这个操作,就可以得到一棵生成树了。具体的证明就算啦
算法实现的难点在于:如何查找树T与其他顶点之间的最小权值的边,
(1)维护一个 u s e d used used数组, u s e d [ i ] used[i] used[i]表示 i i i是否已经在生成树中
(2)维护一个 m i n c o s t mincost mincost数组, m i n c o s t [ i ] mincost[i] mincost[i]表示从已选生成树出发到每个顶点的最小权值。
每次遍历 m i n c o s t mincost mincost数组,选没有 u s e use use的而且cost最小的那个点 v v v,将顶点 v v v加入生成树中,最后,更新 m i n c o s t mincost mincost数组的所有距离(因为新加入一个点 v v v,更新所有距离为 m i n c o s t [ i ] = m i n ( m i n c o s t [ i ] , d i s [ v , i ] ] ) mincost[i]=min(mincost[i], dis[v,i]]) mincost[i]=min(mincost[i],dis[v,i]]

#define MAX 1555
#define inf 1e9

int cost[MAX][MAX];	// cost[u][v]表示边e=(u,v)的权值( 不存在的情况下设为INF)
int mincost[MAX];   // 从集合X出发的边到每个顶点的最小权值
bool used[MAX];		// 顶点i是否包含在集合X中
int V;				// 顶点数

int prim(int s) {
	//1:初始化
	memset(used, 0, sizeof(used));
	fill(mincost, mincost + MAX, inf);
	mincost[s] = 0; int res = 0;

	while (true) {
		int v = -1;
		//2:找到没使用过的,而且当前距离T集合最近的
		for (int i = 0; i < V; i++) {
			if (!used[i] && (v == -1 || mincost[i] < mincost[v]))
				v = i;
		}
		if (v == -1)break;//找不到就结束
		used[v] = 1;
		res += mincost[v];
		//3:从这里开始再更新一遍最短距离
		for (int i = 0; i < V; i++)
			mincost[i] = min(mincost[i], cost[v][i]);
	}
}

Kruskal算法

Kruskal算法按照边的权值的顺序从小到大査看一遍,如果不产生圈
(重边等也算在内 ),就把当前这条边加人到生成树中。至于这个算法为什么是正确的,其实和Prim算法证明的思路基本相同,在此就不详细说明了。

这里的重点问题在于产生圈如何判断,此时使用到了基础的并查集,每次加入一个点就将点与生成树点集中任何一个点进行union,如果两个节点已经同根了,那么就会产生圈。

#define inf 10000000
#define MAX 2005  //边的最大数目
#define p pair<int,int>

struct edge {
	int from, to, cost;//只需要存储边的关系
	bool operator<(const edge &  e) { return cost < e.cost; }
};
vector<edge>G;
int V, E;//顶点数目与边的数目
int root[MAX];

int find(int x) {
	if (x == root[x])return x;
	else return root[x] = find(root[x]);
}

void unite(int x, int y) {
	root[x = find(x)] = root[find(y)];//双跟合并
}
int Kruskal() {
	int res = 0;
	sort(G.begin(), G.end());
	for (int i = 0; i <= V; i++)root[i] = i;//并查集的初始化
	for (unsigned i = 0; i < G.size(); i++) {
		edge e = G[i];
		if (find(e.from) != find(e.to)) {//两个点不在一个集合中,加上该边不会导致环
			unite(e.from, e.to);//合并到一个集合
			res += e.cost;
		}
	}
	return res;
}

最小生成树模板题
最大生成树模板题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值