最小生成树学习笔记
什么是最小生成树
一个图中可能存在多条相连的边,我们从一个图中挑出一些边生成一棵树(树就是指一个无向连通图不包含回路(连通图中不存在环))。
这仅仅是生成一棵树,还未满足最小,当图中每条边都存在权重时,这时候我们从图中生成一棵树(n - 1 条边)时,生成这棵树的总代价就是每条边的权重相加之和。
Kruskal
首先我们先将这几个点每个独自成为一个集合,将一些连接这些集合的边,按照权值从小到大排序,每次加入最小的边,将边两端的集合合并成一个集合,但要保证这两个点不在同一个集合,而怎么判断他们有没有联通呢,这时,我们就可以用到并查集了。
并查集
详见博客(暂无)
然后如果他们在同一个集合就换下一条边。题目详见(P3366)
code
#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
inline int read() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
int cnt,sum,f[maxn],n,m;
struct rode{
int x,y,z;
}a[maxn];
int find(int k){
if(f[k]==k) return k;
return f[k]=find(f[k]);
}
bool cmp(rode a,rode b){
return a.z<b.z;
}
int main () {
cin>>n>>m;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
cin>>a[i].x>>a[i].y>>a[i].z;
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++){
int fx=find(a[i].x),fy=find(a[i].y);
if(fx==fy) continue;
f[fy]=fx;
sum+=a[i].z;
cnt++;
if(cnt==n-1) break;
}
if(cnt!=n-1) cout<<"orz";
else cout<<sum;
return 0;
}
Prim
Prim的主要思路如下:
1.首先从任意一个顶点开始构造最小生成树,并标记那些点已经加入了最小生成树。
2.用dis(假设是dis)存储每个点离生成树的距离。
3.选出离生成树最近的定点加入到生成树中。
一直循环,直到生成树有n个顶点。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,min,t1,t2,t3;
int e[10005][10005],book[1000005]={0},dis[1000005];
int inf=INT_MAX;
int cnt=0,sum=0;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) e[i][j]=0;
else e[i][j]=inf;
for(int i=0;i<m;i++){
scanf("%d%d%d",&t1,&t2,&t3);
e[t1][t2]=t3;
e[t2][t1]=t3;
}
for(int i=1;i<=n;i++)
dis[i]=e[1][i];
int i,j,k;
book[1]=1;
cnt++;
while(cnt<n){
min=inf;
for(i=1;i<=n;i++)
if(book[i]==0&&dis[i]<min){
min=dis[i];
j=i;
}
book[j]=1;
sum+=dis[j];
cnt++;
for(k=1;k<=n;k++)
if(book[k]==0&&dis[k]>e[j][k])
dis[k]=e[j][k];
}
printf("%d\n",sum);
return 0;
}
、
6666