1.概览
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
2.普利姆算法简单描述
1).输入:一个加权连通图,其中顶点为n个,一共有边为m个;
2).初始化:开个数组mp[i][j]记录i到j这条边的权值为多少 一般初始化为一个极大值
3).输如i到j的权值 用mp[i][j]来记录 同时 mp[j][i]=mp[i][j] 两边权值相同
4).接下来就是最为重要的pri函数 求出最小生成树 过程如下所示
假如我们从a点开始求它的最小生成树 以a为集合 此时找与它的连着的最小的权值
一目了然 明显是a e了
所以我们把e加入到a的这个集合 并且以e开始找与它连着的最小的权值
这样d也加入了a e 这个集合 之后我们重复上面的操作一直可以查到b
把b加入到集合中 但发现四周并没有路可走了(走的话就变成一个回路 形成环了)
这样我们就要倒回到开头可以走的 发现eg df并没有连接 让g f 加入此集合
这样就形成了最小生成树了。。
4.克鲁斯卡尔算法代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cmath>
#include <queue>
#include <string>
#include <vector>
#include <set>
using namespace std;
#define ll long long
#define sc(x) scanf("%d",&x)
#define dsc(x,y) scanf("%d%d",&x,&y)
#define pr(x) printf("%d\n",x)
#define FOR(i,n,o) for(int i=o;i<=n;i++)
#define lcr(a,b) memset(a,b,sizeof(a))
#define Inf 1<<29
#define maxn 1005
int m,n;
int vis[maxn],mp[maxn][maxn],dis[maxn];
int pri()
{
int sum=0;
FOR(i,n,1)
{
dis[i]=mp[1][i];//初始化dis
}
vis[1]=1;
FOR(i,n-1,1)
{
int to=-1;
int minn=Inf;
FOR(j,n,1)
{
if(!vis[j]&&dis[j]<minn)
{
minn=dis[j];
to=j;
}
}
if(to==-1)
return -1;//一轮中都进入不到if判断中 就说明无法生成最小生成树
vis[to]=1;//标记为已经用过
sum+=minn;
FOR(j,n,1)
{
dis[j]=min(dis[j],mp[to][j]);//替换dis
}
}
return sum;//返回
}
int main()
{
while(~dsc(n,m))
{
lcr(vis,0);
FOR(i,n,1)
{
FOR(j,n,1)
{
mp[i][j]=Inf;//初始化极大值 浪费空间 有些用不到
}
}
FOR(i,m,1)
{
int f,t,v;
scanf("%d %d %d",&f,&t,&v);
mp[f][t]=v;
mp[t][f]=v;
}
int ans=pri();
pr(ans);
}
return 0;
}
3.克鲁斯卡尔算法简单描述
1).输入:一个加权连通图,其中顶点为n个,一共有边为m个;
2).初始化:开个数组p[i]数组来记录i的祖先是谁 初始化自己的祖先是自己(采用了并查集)
3).输如i到j的权值 用mp[i][j]来记录 同时 mp[j][i]=mp[i][j] 两边权值相同
4).按照给定的权值从小到大排序
5).接下来就是最为重要的kulus 求出最小生成树 并返回 具体如下讲解
排序完好AC 对应在第一个位置 发现A C的祖先都是自己所以sum可以加上他们的权值
然而标记A的祖先为C(并查集的思想)
之后FD F D的祖先也是他们自己 所以加上他们的权值 标记
F的显示为D
重复上面的步骤 若祖先是同一个 就不能添加 最终形成如下的
4.克鲁斯卡尔算法代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <cmath>
#include <queue>
#include <string>
#include <vector>
#include <set>
using namespace std;
#define ll long long
#define sc(x) scanf("%d",&x)
#define dsc(x,y) scanf("%d%d",&x,&y)
#define pr(x) printf("%d\n",x)
#define FOR(i,n,o) for(int i=o;i<=n;i++)
#define lcr(a,b) memset(a,b,sizeof(a))
#define Inf 1<<29
int n,m,fth[100],sum,tol;
struct node
{
int f;
int t;
int v;
}edge[100];
int cmp(node a,node b)
{
return a.v<b.v;
}
int find(int x)
{
if(fth[x]==x)
{
return fth[x];
}
return fth[x]=find(fth[x]);//状态压缩
}
int kulus()
{
sum=0;
tol=0;
FOR(i,m,1)
{
int f=edge[i].f;
int t=edge[i].t;
if(find(f)!=find(t))//判断祖先是否相同
{
fth[find(f)]=find(t);
sum+=edge[i].v;
pr(sum);
tol++;
}
if(tol==n-1)//只需要找到n-1条边相加
return sum;
}
return -1;//无法形成最小生成树
}
int main()
{
while(~dsc(n,m))
{
FOR(i,n,1)
{
fth[i]=i;//初始化自己祖先为自己
}
FOR(i,m,1)
{
sc(edge[i].f);
sc(edge[i].t);
sc(edge[i].v);
}
sort(edge+1,edge+1+m,cmp);//按权值从小到大排序
int ans=kulus();
pr(ans);
}
return 0;
}
END!!!!!!!!!!!!!!!!!!!!!!