目录
例题:代码源 最小生成树1
prim
思想
首次初始化每个点的距离为0x3f3f3f3f,先任意找一个点,然后遍历剩下的所有的点找到距离这个点最近的点,更新距离,把这个点标记,继续用这个点更新与他相连并且未标记点的距离, 距离为两个相连点的边,然后继续遍历未标记的点加入集合,遍历n-1次。
与dijkstra的区别:dist[]数组表示被标记点和当前点的最短距离,而不是与原点
朴素做法 n^2+m
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1001;
struct node{
int y,v;
node(int _y,int _v){
y=_y,v=_v;
};
};
vector<node> edge[N];
int n,m,dist[N];
bool b[N];
void prim(){
memset(b,false,sizeof b);
memset(dist,127,sizeof dist);
dist[1]=0;
int ans=0;
for(int i=1;i<=n;i++){
int x=-1;
for(int j=1;j<=n;j++){
if(!b[j]&&dist[j]<1<30){
if(x==-1||dist[j]<dist[x]){
x=j;
}
}
}
b[x]=true;
ans+=dist[x];
for(auto j:edge[x]){
dist[j.y]=min(dist[j.y],j.v);
}
}
cout<<ans<<"\n";
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
edge[x].push_back(node(y,z));
edge[y].push_back(node(x,z));
}
prim();
return 0;
}
set优化 (n+m) logn
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 50005;
struct node{
int y,v;
node(int _y,int _v){
y=_y,v=_v;
};
};
set<pii> q;
vector<node> edge[N];
int n,m,dist[N];
bool b[N];
void prim(){
memset(b,false,sizeof b);
memset(dist,127,sizeof dist);
dist[1]=0;
q.clear();
for(int i=1;i<=n;i++){
q.insert({dist[i],i});
}
int ans=0;
for(int i=1;i<=n;i++){
int x=q.begin()->second;
q.erase(q.begin());
ans+=dist[x];
b[x]=true;
for(auto j:edge[x]){
if(!b[j.y]&&j.v<dist[j.y]){
q.erase({dist[j.y],j.y});
dist[j.y]=j.v;
q.insert({dist[j.y],j.y});
}
}
}
cout<<ans<<"\n";
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
edge[x].push_back(node(y,z));
edge[y].push_back(node(x,z));
}
prim();
return 0;
}
kruskal
思想
按边权从小到大排序,每次枚举一条边连接的两个点,如果两个顶点不在同一个集合,就合并集合,如果在同一个集合,就不做,直到最后集合为1,就结束。(如果本身知道是联通块,就遍历结束就一定存在最小生成树,不需要判断)
朴素做法 mlogm
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5+5;
struct node{
int x,y,v;
bool operator<(const node &A) const{
return v<A.v;
}
}a[N];
int n,m,fa[50001];
int findset(int i){
if(i==fa[i]){
return i;
}
return fa[i]=findset(fa[i]);
}
void kruskal(){
for(int i=1;i<=n;i++){
fa[i]=i;
}
sort(a+1,a+m+1);
int ans=0,cnt=n;
for(int i=1;i<=m;i++){
int x=findset(a[i].x),y=findset(a[i].y);
if(x!=y){
fa[x]=y;
ans+=a[i].v;
cnt--;
}
}
if(cnt==1) cout<<ans<<"\n";
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i].x>>a[i].y>>a[i].v;
}
kruskal();
return 0;
}