最小生成树
定义
- 树
连通,但不成环
- 最小
边的代价和最小
规律
举例
以四个点为例,共需三条线即可。
- 同类共四种(任意去掉其中一条线)
- 同类共四种
- 同类共四种(任意去掉其中一条线)
- 同类共四种(任意去掉其中一条线)
- 同类共四种(任意去掉其中一条线)
总结
n个点生成树需要(n-1)条线来连通。
克鲁斯卡尔 Kruskal
贪心思想+并查集(检查是否成环)
- 用边权按从小到大排序
- 初始化并查集
- 循环加边,检查是否成环 (n-1条)
普莱姆 Prim
运用了,贪心思想
贪心:
一种结果很准确,但是占的空间很大很大。
所以我们这次要用这种很好的方法来做。
Prim
Prim算法跟Dij和Bellman-算法一样。
也是使用了”蓝白点”的思想
Prim算法每次循环都要将一个蓝点u变为一个白点,这样循环,min[u]里面的权还是最小的
初始化
对于flag操作的问题:
memset(flag,true,sizeof flag);
解释:
把flag全部填为真(True)
对于f
for (int i=1;i<=n;++i) f[i]=0x7fffffff; f[1]=0;
注意哦,这里不能用memset,那样会卡爆!!
解释:
如果呢?到了
对于map
memset(map,0,sizeof map);
2.循环
for (int i=1;i<=n-1;++i){
//1.在f中查找最小权值的编号(且flag==false)
int min1=0x7fffffff;
int v=-1;
for (int j=1;j<=n;++j){
if (flag[j] && min1>f[j]){
min1=f[j];
v=j;
}
}
//2.标记v
flag[v]=false;
//3.更新v发出的边
for (int j=1;j<=n;++j){
if (flag[j] && map[v][j]!=0){
if (map[v][j]<f[j]) f[j]=map[v][j];
}
}
}
附上完整程序
//邻接矩阵
#include <cstdio>
#include <cstring>
const int N=110;
int map[N][N],n,m,x,y,z,f[N];
bool flag[N];
int main(){
freopen("prim.in","r",stdin);
freopen("prim.out","w",stdout);
scanf("%d%d",&n,&m);
memset(map,0,sizeof map);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
map[x][y]=z; map[y][x]=z;
}
memset(flag,true,sizeof flag);
for (int i=1;i<=n;++i) f[i]=0x7fffffff; f[1]=0;
for (int i=1;i<=n-1;++i){
//1.在f中查找最小权值的编号(且flag==false)
int min1=0x7fffffff;
int v=-1;
for (int j=1;j<=n;++j){
if (flag[j] && min1>f[j]){
min1=f[j];
v=j;
}
//更新我的指令
//
}
//2.标记v
flag[v]=false;
//3.更新v发出的边
for (int j=1;j<=n;++j){
if (flag[j] && map[v][j]!=0){
if (map[v][j]<f[j]) f[j]=map[v][j];
}
}
}
int ans=0;
for (int i=1;i<=n;++i) ans+=f[i];
printf("%d",ans);
return 0;
}