最小生成树

/* 
   kruskal算法   求取MST
   首先按照w权重对于所有的边从小到大进行排序
   按照最小生成树的定义来讲,就是使得所有的边上的权重总和最小
   由于是一棵树,所以不能形成环  
   判断是否为环 就是找他们的祖先,如果祖先是相同的,就证明一个是在最后,祖先是在最前,
   他们在同一条链上,然后这边上两个点 相连就变成一个环了 
   然后的话就是N条边只需要选取最短的M-1条边就可以组成一颗最小生成树
*/ 
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn= 1e3;///1000的大小 
int f[maxn];///f(i)代表节点 i 的父亲 
struct min_edge{
  int u;///起点
  int v;///终点
  int w;///两点之间的距离
}edge[maxn];
bool cmp(struct min_edge A,struct min_edge B)///按照W的大小从小到大排序 
{
    return A.w < B.w;
}
int  FA_find(int x)///找出他们的公共的祖先(最高层次的)
{
    if (x!=f[x])
         return FA_find(f[x]);///开始我们赋值就是他本身,如果他的父亲是他本身,就说明他就是祖先 
         ///不是的话找他父亲的父亲 
    return x;
}
int main()
{
    ///freopen("3.txt","r",stdin);
    ///freopen("4.txt","w",stdout);
    int n;///n条边
    int m;///m个节点
    cin>>n>>m;
    for (int i=1;i<=n;++i)
    {
      cin>>edge[i].u>>edge[i].v>>edge[i].w;
    }
    sort(edge+1,edge+1+n,cmp);///按照w的大小从小到大排序 
    for (int i=1;i<=m;++i)f[i] = i;///首先的话节点的祖宗就是自身 
    int cnt = 0;///记录当前选了多少条边
    for (int i=1;i<=n;++i)///n条边选取m-1条 
    {
        if (cnt == m-1)break;
        if (FA_find(edge[i].u)!=FA_find(edge[i].v))///这条边上的起点和终点的祖先不同就说明这两个点之间可以连接这条边 
        {
            f[FA_find(edge[i].v)] = FA_find(edge[i].u);///将这条边放入我们所建的最小生成树  此时V节点的父亲要变成U的祖先
	    ///因为他们现在在一条链上了 
            printf("%d %d
",edge[i].u,edge[i].v);
            ++cnt;
        }
    }
    return 0;
}

prim此处用邻接矩阵存图,没有判断重边 有需要可以自判 或者改用vector或者链式前向星

/*
  prim算法  加点操作 首先确定一个节点,然后找到离他最近的节点B    普里姆算法
  接下来找到离节点B最近的节点  如此循环回溯
  但是走过的节点需要标记一下 
  
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn= 1e3;
const int INF=0x3f3f3f3f;/// 一个极大值 
int disc[maxn];///储存最短距离  disc[i] 第i个节点最短距离  初始值就是节点1到各个点之间的距离W ,不存在就是INF(无穷大) 
int close[maxn];///储存最短的连接点  close[i]代表离节点i最近的那个节点 
int G[maxn][maxn];///邻接矩阵  G[i][j]起点i 终点j上存在一条重w的边 
bool vis[maxn];///标记数组,如果当前的节点被找过就为1 
int  main()
{
    /// freopen("1.txt","r",stdin);
    ///freopen("2.txt","w",stdout);
	///ios::sync_with_stdio(false);
	///cin.tie(0);
	///cout.tie(0);
	memset(vis,false,sizeof(vis));///相当于for(int i-0;i<=n;++i) vis[i]=false; 
	memset(G,INF,sizeof(G));///同理   因为你要找最短的距离,所以不存在的边就是距离无穷大 
	int n;///n条边
	int m;///m个节点
	cin>>n>>m;
	for (int i=0;i<n;++i)
	{
		int u,v,w;
		cin>>u>>v>>w;///起始点 终点  权重
		G[u][v] = w;///存边
		G[v][u] = w;///存边   无向图两条边 
	}
	for (int i=1;i<=n;++i)
	{
		disc[i] = G[1][i];///首先都储存起点为1终点为i的边 
		//cout<<disc[i]<<endl;
		close[i] = 1;///离i最近的此时的边就是1 
	}
	int idx=1;///距离最短的节点下标
	for (int i=1;i<m;++i)
	{
		int mine=INF;///由于要找到最小值  所以要赋值一个最大的数值 
		vis[idx] = true;///当前这个最小的节点用过  所以标记为1 
		for (int j=1;j<=m;++j) 
		{
			if (!vis[j]&&mine>disc[j])///从没有选过的最短路径中找到离 当前节点最近的那个节点的下标idx 
			{
				idx = j;
				mine = disc[j];
			}
		}
        if (mine ==  INF){puts("No construct");return 0;}/// 原图不联通  无法找出最小生成树
		cout<<idx<<"->"<<' '<<close[idx]<<endl;
     	//	cout<<disc[idx]<<endl;
		for (int k=1;k<=m;++k)///找到离当前节点最近的下标idx 
		{
			if (G[idx][k]<disc[k]&&!vis[k])///更新disc 从idx出发  如果这条边到k的距离小于刚刚那个节点的就可以直接更新 
			{
				disc[k] = G[idx][k];
				close[k] = idx;///此时离他最近的就是idx节点 
			}
		}
	}
	cout<<endl;
	return 0;
}

#include <bits/stdc++.h>
#define ll  long long
using namespace std;
const int maxn= 1e6;
const int INF=INT_MAX;/// 一个极大值 
int disc[maxn];///储存最短距离  disc[i] 第i个节点最短距离  初始值就是节点1到各个点之间的距离W ,不存在就是INF(无穷大) 
int close[maxn];///储存最短的连接点  close[i]代表离节点i最近的那个节点 
struct Node {
    int v;
    int w;
    Node(int vv=INF,int ww=INF):v(vv),w(ww){}
};
vector<Node>G[maxn];/// vector建图
bool vis[maxn];///标记数组,如果当前的节点被找过就为1 
int all = 0;
signed   main()
{
    ///freopen("1.txt","r",stdin);
    ///freopen("2.txt","w",stdout);
	///ios::sync_with_stdio(false);
	///cin.tie(0);
	///cout.tie(0);
	memset(vis,false,sizeof(vis));///相当于for(int i-0;i<=n;++i) vis[i]=false;
    memset(disc,0x7f,sizeof(disc));
	int n;///n条边
	int m;///m个节点
	cin>>m>>n;
    for(int i=1; i<=m; i++)
    {
        G[i].clear();
    }
    for (int i=0;i<n;++i)
    {
	int u,v,w;
	cin>>u>>v>>w;///起始点 终点  权重
	G[u].push_back(Node(v,w));
        G[v].push_back(Node(u,w));
    } 
    ///for (int i=1;i<=m;++i)disc[i] = INF;
    for (int i=0;i<G[1].size();++i)
    {
        disc[G[1][i].v] = min(G[1][i].w,disc[G[1][i].v]);///首先都储存起点为1终点为i的边  记得判断重边
        ///cout<<disc[i]<<endl;
	//cout<<disc[i]<<endl;
	close[G[1][i].v] = 1;///离i最近的此时的边就是1 
    }
    vis[1] = true;
    for (int i=1;i<m;++i)
    {
	int mine=INF;///由于要找到最小值  所以要赋值一个最大的数值 
        int idx = -1;
	for (int j=1;j<=m;++j) 
	{
             	if (!vis[j]&&mine>disc[j])///从没有选过的最短路径中找到离 当前节点最近的那个节点的下标idx 
		{
			idx = j;
			mine = disc[j];
		}
	}
        if (mine ==  INF){puts("orz");return 0;}/// 原图不联通  无法找出最小生成树
        all += mine;
        vis[idx] = true;
	///cout<<idx<<"->"<<' '<<close[idx]<<endl;
     	///cout<<disc[idx]<<endl;
        for (int k=0;k<G[idx].size();++k)
        {
            if (!vis[G[idx][k].v]&&G[idx][k].w<disc[G[idx][k].v])///更新disc 从idx出发  如果这条边到k的距离小于刚刚那个节点的就可以直接更新 
	    {
		disc[G[idx][k].v] = G[idx][k].w;
		close[G[idx][k].v] = idx;///此时离他最近的就是idx节点 
	    }
        }
   }
   cout<<all<<endl;
   return 0;
}

齐芒行,川锋明!
原文地址:https://www.cnblogs.com/qimang-311/p/14092086.html