prim:每次找距离当前生成树最近的点,添加到生成树中,并更新其余距离
krustral:将边权排序,从小到大枚举,如果两个点不在同一个集合中,就合并,使用并查集
最小生成树:包含全部n个顶点,且连通
联系:Dijkstra算法是更新到起始点的距离,Prim是更新到集合S的距离
prim
朴素版 类似dijkstra 每次找不在生成树中的距离生成树距离最近的点添加到生成树中
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int g[N][N];
int n,m;
int dist[N];
bool st[N];
void prim()
{
int ans=0;
memset(dist,0x3f,sizeof dist);
dist[1]=0;//无向图 可以随便找一个点做起点 如果是有向图则不可以随便找一个点!!!
for(int i=1;i<=n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(st[j]==0&&(t==-1||dist[j]<dist[t])) t=j;
}
st[t]=1;
if(dist[t]==0x3f3f3f3f) //没有生成树 不连通 就会导致这个结果 剩下一个点其余点都不能到达
{
puts("impossible");
return;
}
ans+=dist[t];
for(int j=1;j<=n;j++)
{
if(!st[j]) dist[j]=min(dist[j],g[t][j]);
}
}
printf("%d",ans);
}
int main()
{
memset(g,0x3f,sizeof g);
scanf("%d%d",&n,&m);
while(m--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x][y]=min(g[x][y],z);
g[y][x]=g[x][y];
}
prim();
}
堆优化
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int g[N][N];
int n,m;
int dist[N];
bool st[N];
typedef pair<int,int> pii;
void prim()
{
int ans=0,res=0;
memset(dist,0x3f,sizeof dist);
dist[1]=0;//无向图 可以随便找一个点做起点 如果是有向图则不可以随便找一个点
priority_queue<pii,vector<pii>,greater<pii>> heap;
heap.push({0,1});
while(heap.size())
{
auto t=heap.top();
heap.pop();
int p=t.second;
if(st[p]) continue;
ans+=t.first,res++;
st[p]=1;
for(int i=1;i<=n;i++)
{
if(dist[i]>g[p][i]) //注意这里 不要写成dist[i]>dist[p]+g[p][i]要与dijkstra区别开
{
dist[i]=g[p][i];
heap.push({dist[i],i});
}
}
}
//printf("res=%d\n",res);
if(res==n) printf("%d",ans);
else puts("impossible");
}
int main()
{
memset(g,0x3f,sizeof g);
scanf("%d%d",&n,&m);
while(m--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x][y]=min(g[x][y],z);
g[y][x]=g[x][y];
}
prim();
}
kruskal
从小到大枚举每条边,端点为a,b, 如果a,b,不连通,就把这条边加入到集合中,可以用并查集
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct Edge{
int a,b,w;
}edge[N];
int n,m;
int f[N];
int cmp(Edge i,Edge j)//注意这里结构体的运算符重载
{
return i.w<j.w;
}
int find(int k)
{
if(k!=f[k]) f[k]=find(f[k]);
//if(k!=f[k]) f[k]=find(k); 这里又写错了
return f[k];
}
void kruskal()
{
int ans=0;
for(int i=1;i<=n;i++) f[i]=i;
sort(edge+1,edge+1+m,cmp);
for(int i=1;i<=m;i++)
{
int x=edge[i].a,y=edge[i].b;
if(find(x)!=find(y))
{
ans+=edge[i].w;
f[find(x)]=find(y);
}
}
//也可以每记录边的数量 最后看边的数量是不是n-1 每次合并一个集合就是添加一条边
for(int i=1;i<=n;i++)
{
if(find(i)!=find(1))
{
printf("impossible");
return;
}
}
printf("%d",ans);
}
int main()
{
scanf("%d%d",&n,&m);
int cnt=0;
for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].w);
//虽然是无向图 但是这里可以就存一个方向的
kruskal();
}