题目
题解
1.Prim算法
Prim的思想是将任意节点作为根,再找出与之相邻的所有边(用一遍循环即可),再将新节点更新并以此节点作为根继续搜
#include<bits/stdc++.h>
using namespace std;
bool b[5001];
int ans=0,dis[5001],w[5001][5001],x,y,z,n,m;
void Prim()
{
//以1作为根节点
for(int i=0;i<=n;i++) dis[i]=w[1][i];
dis[1]=0;
b[1]=true;//标记1已经访问
for(int i=1;i<n;i++)
{
int k=0;
for(int j=1;j<=n;j++)
{
//如果j还没有访问过且两点之间可连接
if(!b[j]&&dis[j]<dis[k])
k=j;//记录j
}
b[k]=true;
ans+=dis[k];
//不断更新根节点
for(int j=1;j<=n;j++)
{
if(dis[j]>w[k][j])
//dis储存从k到j的最短距离
dis[j]=w[k][j];
}
}
printf("%d\n",ans);
}
int main()
{
scanf("%d %d",&n,&m);//输入点数和边数
for(int i=0;i<=n;i++)
{
//从i到j的边,若i,j重合,距离为0,不重合,统一赋值为正无穷
for(int j=i+1;j<=n;j++)
w[i][j]=w[j][i]=0x7fffffff;
w[i][i]=0;
}
while(m--)
{
scanf("%d %d %d",&x,&y,&z);//起点,终点,权值
w[x][y]=w[y][x]=min(w[x][y],z);//初始赋值
}
Prim();
return 0;
}
2.Kruskal算法
Kruskal算法的思想是优先选取权值较小的边,并依次连接,若出现环则跳过此边(用并查集来判断是否存在环)继续搜,直到已经使用的边的数量比总点数少一即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,sum=0,k=0;//n端点总数,m边数,tot记录最终答案,k已经连接了多少边
int fat[200010];//记录集体老大
struct A
{
int from;
int to;
int dis;
}edge[200010];
bool cmp(A &a,A &b)
{
return a.dis<b.dis;
}
/*返回团体老大,明确归属*/
int father(int x)
{
if(fat[x]!=x)
return father(fat[x]);
else return x;
}
/*把y加入x团体*/
void unionn(int x,int y)//x为边的起点,y为边的终点
{
fat[father(y)]=father(x);//x为y在团体中老大
}
int main()
{
scanf("%d%d",&n,&m);//输入点数,边数
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis);//起,终,权值
}
for(int i=1;i<=n;i++)
fat[i]=i;//自己最开始就是自己的老大 (初始化)
sort(edge+1,edge+1+m,cmp);//按权值排序(kruskal的体现)
for(int i=1;i<=m;i++)//从小到大遍历
{
if(k==n-1) break;//n个点需要n-1条边连接
if(father(edge[i].from)!=father(edge[i].to))//假如不在一个团体
{
unionn(edge[i].from,edge[i].to);//加入
sum+=edge[i].dis;//记录边权
k++;//已连接边数+1
}
}
printf("%d\n",sum);
return 0;
}