Prim算法
算法思路:
1.对于所有的点,可以看作两个集合A和B,A中的点都是在最小生成树中的点,B是未加入最小生成树中的点。初始时随机选择一个点加入到A中,更新集合A到其他点的距离。
PS:集合A到其他点的距离比较抽象,这里举一个例子。点b是集合B中的一个点,现在更新集合A到b的点的距离,这个距离就是b与集合A中离他最近的那个点的距离。
2.每次选出一个离集合A最近的点,加入到集合A中,加上权值,然后更新集合A到其他点的距离。
3.更新n-1次就可以找到一个最小生成树了。
时间复杂度为:O(N^2)
空间复杂度为: O(N^2)
题目链接:【模板】最小生成树 - 洛谷
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 5e3+10;
int dis[maxn];
int mp[maxn][maxn];
int vis[maxn];
int n,m;
void prim()
{
long long sum = 0;
memset(dis,inf,sizeof(dis));
dis[1]=0;
for(int i = 1;i<=n;i++)
{
int id = -1,minn = inf;
for(int j = 1;j<=n;j++)
{
if(dis[j]<minn&&!vis[j])
{
minn=dis[j];
id=j;
}
}
if(minn==inf)
{
cout<<"orz"<<endl;
return ;
}
vis[id]=1;
sum+=minn;
for(int j = 1;j<=n;j++)
{
if(!vis[j]&&mp[id][j]!=inf)
{
if(dis[j]>mp[id][j])
{
dis[j]=mp[id][j];
}
}
}
}
cout<<sum<<endl;
}
int main()
{
int u,v,w;
cin>>n>>m;
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
if(i==j)
mp[i][j]=0;
else
mp[i][j]=inf;
}
}
for(int i = 1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
mp[u][v]=mp[v][u]=min(mp[u][v],w);
}
prim();
return 0;
}
Kruskal算法
算法思路:
1.用结构体数组保存输入的边的起始点,终止点以及边的权值。使用sort对结构体按边的权值从小到大排序。
2.使用一个记录变量num记录已选择的边的数量,连接n个点的最小生成树一定有n-1条边。所以如果遍历完所有边后,num<n-1那么说明组成不了最小生成树。
3.遍历结构体数组,使用并查集记录在最小生成树中的点
a)如果点未在最小生成树中,就把他加入最小生成树,更新并查集,加上权值
b)如果点已经在最小生成树中,就略过它
时间复杂度:O(eloge)
空间复杂度O(m)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
struct node
{
int u,v,w;
}a[maxn];
bool cmp(struct node x,struct node y)
{
return x.w<y.w;
}
int f[5010];
int root(int x)
{
if(f[x]==x)
{
return x;
}
return f[x]=root(f[x]);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1;i<=n;i++)
{
f[i]=i;
}
for(int i = 1;i<=m;i++)
{
cin>>a[i].u>>a[i].v>>a[i].w;
}
sort(a+1,a+1+m,cmp);
long long sum =0;
int num = 0;
for(int i = 1;i<=m;i++)
{
int rx = root(a[i].u);
int ry = root(a[i].v);
if(rx!=ry)
{
f[rx]=ry;
sum+=a[i].w;
num++;
if(num==n-1)
{
break;
}
}
}
if(num==n-1)
{
cout<<sum<<endl;
}
else
{
cout<<"orz"<<endl;
}
return 0;
}