Minimum Spanning tree MST最小生成树 <Template>

1 篇文章 0 订阅
1 篇文章 0 订阅

总结一下最小生成树,并且放一下模板。

假设 n 表示点数,m 表示边数。

Prim算法

适用条件:

适用于稠密图,时间复杂度 O(n^2)。

核心思想:

每次挑一条与当前集合相连的最短边。

代码
//prim 算法  类似与dijkstra算法   总是维护最小生成树的一部分
//prim 算法适用于稠密图,  kruskal算法适用于 稀疏图


//假设现在已经求得的生成树的顶点的集合是x

int cost [max_v][max_v];    //cost[u][v]表示边e=(u,v)的权值(不存在的时候设为inf)
int mincost[max_v];         //从集合x出发的边到每个顶点的最小权值
bool used[max_v];           //顶点i是否包含在集合x中
int V;                      //顶点数
int prim(){
	for(int i = 0; i < V; i++){
		mincost[i] = INF;
		used[i] = false;
	}
	mincost[0] = 0;
	int res = 0;
	while(true){
		int v = -1;
		//从不属于x的顶点中选取从x到其权值最小的顶点
		for(int u = 0; u < V; u++){
			if(!used[u] && (v == -1|| mincost[u] < mincost[v]))v = u;
		}
		if(v == -1 )break;
		used[v] = true; //把顶点v加入x
		res += mincost[v]; //把边的长度加到结果里
		for(int u = 0; u < V;u++){
			mincost[u] = min(mincost[u],cost[v][u]);
		}
	}
	return res;
}
堆优化的prim
//prim 堆优化
// 在不确定有多少条边的时候不好用,建议还是用原始的prim算法
int N,M;//N个点,M个边
const int V_MAX = 100;
const int INF = 1<<31 - 1;
 
typedef struct edge
{
    int to, cost;
    edge(int a, int b){
        to = a;
        cost = b;
    }
}edge;
typedef pair<int ,int> P;
 
vector<edge > G[V_MAX];
int used[V_MAX];
 
int prim()
{
    int sum = 0;
    priority_queue<P, vector<P>, greater<P> > pque;
    pque.push(P(0,1));
 
    while(!pque.empty())
    {
        P temp = pque.top();
        pque.pop();
        int V = temp.second;
        int cos = temp.first;
        if(used[V])continue;
        sum += cos;
        used[V] = 1;
        for(int i=0; i<G[V].size(); i++)
        {
            edge e = G[V][i];
            pque.push(P(e.cost, e.to));
        }
    }
    return sum;
}
 
int main()
{
    scanf("%d%d",&N,&M);
    for(int i=0; i<M; i++)
    {
        int fir,sec,thi;
        scanf("%d%d%d",&fir, &sec, &thi);
        G[fir].push_back(edge(sec,thi));
        G[sec].push_back(edge(fir,thi));
    }
    cout << prim() << endl;
}

Kruskal算法

适用条件:

适用于稀疏图,时间复杂度 O(mlogm)。

核心思想:

从小到大挑不多余的边。

代码:
const int maxn=1e4+5;
typedef long long LL;
int father[maxn];
struct node
{
    int u,v,val;//u-v之间边权为val
}edge[maxn];
bool cmp(node a,node b)//按照边权小到大排序
{
    return a.val<b.val;
}
int Find(int x)//并查集查找父节点
{
    if(father[x]==x) return father[x];
    return father[x]=Find(father[x]);
}
void Union(int x,int y)//合并
{
    int fa=Find(x);
    int fb=Find(y);
    if(fa!=fb) father[fb]=fa;
}
int kruskal(int n,int m)
{
    sort(edge,edge+m,cmp);
    for(int i=0;i<=n;i++) father[i]=i;//初始化并查集
    int ans=0;
    for(int i=0;i<m;i++)
    {
        int fa=Find(edge[i].u);
        int fb=Find(edge[i].v);
        if(fa!=fb)//如果u,v属于不同集合,进行合并,加入边权
        {
            Union(edge[i].u,edge[i].v);
            ans+=edge[i].val;
        }
    }
    return ans;
}

当时直接套并查集写的,显得有点冗余

题目的话,以后有时间再放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
prim_mst构造最小生成树的思想是通过贪心算法逐步选择边来构建最小生成树。具体步骤如下: 1. 创建一个空的最小生成树MST和一个空的集合visited,用于存储已经访问过的顶点。 2. 选择一个起始顶点作为根节点,并将其加入visited集合。 3. 从根节点开始,遍历与根节点相邻的边,并选择权重最小的边。 4. 将选择的边加入MST,并将边的另一个顶点加入visited集合。 5. 重复步骤3和步骤4,直到visited集合包含所有顶点。 6. 最终得到的MST就是原图的最小生成树。 以下是使用prim_mst构造最小生成树的示例代码: ```python import numpy as np def prim_mst(graph): num_vertices = len(graph) MST = [] visited = set() # 选择起始顶点 start_vertex = 0 visited.add(start_vertex) while len(visited) < num_vertices: min_weight = np.inf min_edge = None # 遍历已访问的顶点 for vertex in visited: # 遍历与已访问顶点相邻的边 for neighbor, weight in enumerate(graph[vertex]): # 如果边的另一个顶点未访问且权重更小,则更新最小边 if neighbor not in visited and weight < min_weight: min_weight = weight min_edge = (vertex, neighbor) # 将最小边加入MST,并将边的另一个顶点加入visited集合 MST.append(min_edge) visited.add(min_edge[1]) return MST # 示例输入矩阵 graph = np.array([[0, 192, 344, 0, 0, 0, 0, 0, 0, 0, 0], [192, 0, 309, 0, 555, 0, 0, 0, 0, 0, 0], [344, 309, 0, 499, 0, 0, 0, 0, 0, 0, 0], [0, 0, 499, 0, 840, 0, 229, 286, 0, 0, 0], [0, 555, 0, 840, 0, 237, 0, 0, 0, 0, 0]]) # 构造最小生成树 MST = prim_mst(graph) print("Minimum Spanning Tree:") for edge in MST: print(edge) ``` 输出结果为最小生成树的边集合。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值