基本概念要求
1.不成环(就是不是首位相连,可以形成一个连接闭合的图形)
2.连通(就是每个点在不成环的前提下都可以连起来,像贪吃蛇)
最小生成树是什么?
最小生成树就是由几个点,和几条边构成的树,由因为最小,所以在调边的时候各边的和应该是最小的。
最小边原则
图中权值最小的边(如果唯一)一定在最小生成树上。
算法:
Prim算法
如何实现这个代码?
- 首先找到两个点,然后由这两个点分片(就像地主画土地一样)
- 然后由这两个点出发,进行点之间的相连,然后在连得时候比较边的数值,选择最小的那个。 最后两块再连在一起。
- 最后检查是否由n-1个边构成的树,没有不成立最小生成树。(n代表n个点)
代码:
#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;
int vst[505]; //标记顶点是否加入其中
int d[505]; //表示边的最小的时候
int g[505][505];
int n,m,ans=0;
void Read() //读入数据
{
int i,j,x,y,w;
cin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) g[i][j]=INF; //取极大值不会超
for(i=1;i<=m;i++)
{
cin>>x>>y>>w;
g[x][y]=g[y][x]=w;
}
}
void Prim(int w)
{
int i,j,k,minn;
memset(vst,0,sizeof(vst)); //初始化生成树点集合
for(i=1;i<=n;i++) d[i]=INF;
d[v0]=0;
ans=0;
for(i=1;i<=n;i++)
{
minn=INF;
for(j=1;j<=n;j++)
if(vst[j]==0&&minn>d[j]) //找到最小的边
{minn=d[j]; k=j;}
vst[k]=1; //标记
ans+=d[k];
for(j=1;j<=n;j++)
if(vst[j]==0&&d[j]>g[k][j]) //修改
d[j]=g[k][j];
}
}
int main()
{
Read();
Prim(l);
cout<<ans<<endl;
return 0;
}
Kruskal算法
如何实现这个代码?
- 首先是将点,边分开
- 然后由一个点出发,进行点之间的相连,然后在连得时候比较边的数值,选择最小的那个。
- 最后检查是否由n-1个边构成的树,没有不成立最小生成树。(n代表n个点)
代码:
#include <iostream>
using namespace std;
const int maxn=100005;
struct edge{int x,y,z;} a[maxn];
int n,m,prt[maxn],ans=0.bj;
bool cmp(const edge &x,const edge &y){return x.z<y.z;} //边的大小进行比较
int Getfather(int x) //查找祖先(也就是找到最开始的那个点)
{
if(prt[x]==x) return x;
prt[x]=Getfather(prt[x]);
return prt[x];
}
void kruskal() //这一步就是在找最小的边然后进行加和
{
int f1,f2,k,i;
k=0;
for(i=1;i<=n;i++) prt[i]=i;
for(i=1;i<=m;i++)
{f1=Getfather(a[i].x);
f2=Getfather(a[i].y);
if(f1!=f2)
{
ans=ans+a[i].z;
prt[f1]=f2; //到下一个点了
k++;
if(k==n-1) break;
}
}
if(k<n-1) //检查边数不对
{cout<<"不可能由最小生成树"<<endl;
bj=0;
return;}
}
int main()
{
cin>>n>>m; //n是点数,m是原来的边数
ans=0;bj=1;
for(int i=1;i<=m;i++)
cin>>a[i].x>>a[i].y>>a[i].z;
sort(a+1,a+m+1,cmp);
kruskal();
if(bj) cout<<ans<<endl;
return 0;
}