最小生成树简述及模板题
先补充一点图论的基本术语(基本复制于维基百科)
1.一个图的顶点集(点集)一般记作
,当不发生混淆时可简记为
。图
的阶为其顶点数目,亦即|
|。以两个顶点
、为端点的边一般记作
、
或
。一条边连接两个顶点u、v时,称u与v相邻。图
的边集一般记作
,当不发生
混淆时可简记为。
2.一个自环是两个端点为同一顶点的边。如果有多于一条边连接同一对顶点,则它们均被称为重边。一个图的重数是重复次数最多
的边的重复次数。如果一个图不含自环或重边,则称为简单图。多数情况下,如无特殊说明,可以假定“图”总是指简单图。
3.连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
4.强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
5.生成树:在无向图中,n个顶点只取n-1条边。生成树是连通图的极小联通子图,即如果再加一条边,必形成环
6.最小生成树:在所有生成树中,边代价和最小的生成树
最小生成树算法
包括prime算法和kruskal算法
一.prime算法,又称“加点法”
1.基本思想:在一个带权连通图中,任取一顶点加入生成树中,标记,遍历所有点,找到离该点距离最近的点,标记,直至所
有顶点都加入到生成树中。(跟dijkstar思想很像)
2.复杂度为O(n^2),n为顶点数,用邻接表存,适用于稠密图
3.上一个模板题 杭电1233 还是畅通工程 http://acm.hdu.edu.cn/showproblem.php?pid=1233
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 110;
const int inf=0x3f3f3f3f;
typedef long long ll;
int mapp[maxn][maxn];
bool vis[maxn];
int dis[maxn];
int n;
int prime(int start){
int ans=0;
for(int i=1; i<=n; i++){
dis[i]=mapp[start][i];//记录起点出发的距离
vis[i]=0;
}
vis[start]=1; //加入任一点(或起点)
int noww;
for(int i=1; i<n; i++){
int minn=inf;
for(int j=1; j<=n; j++){
if(!vis[j] && dis[j]<minn){//找到距离最短的
minn=dis[j];
noww=j;
}
}
vis[noww]=1;
ans+=minn;
for(int j=1; j<=n; j++){
if(!vis[j] && dis[j]>mapp[noww][j])//加入新的点后更新距离
dis[j]=mapp[noww][j];
}
}
return ans;
}
int main() {
int u,v,d;
while(~scanf("%d",&n)){
if(n==0) break;
for(int i=1; i<=n; i++){
for(int j=1; j<=n; j++){
mapp[i][j]=inf;
}
}
for(int i=1; i<= n*(n-1)/2; i++){
scanf("%d%d%d",&u,&v,&d);
mapp[u][v]=d;
mapp[v][u]=d;
}
int ans=prime(1);
printf("%d\n",ans);
}
return 0;
}
二.kruskal算法,又称“加边法”
1.基本思想:将边按权值从小到大排序,遍历,若该边两个端点不在一个树上,则加上,若在,则跳过继续下一条边,贪心思
想,并查集实现
2.时间复杂度O(mlogm),m为边数,用邻接表存
3.还是上一个题
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e4+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m;
struct node{
int u,v,val;
}s[maxn];
int fa[maxn],deep[maxn];
bool cmp(node x,node y){
return x.val<y.val;
}
void init(){
for(int i=0; i<=m; i++){
fa[i]=i;
deep[i]=1;
}
}
int findd(int x){
return fa[x]==x? x:x=findd(fa[x]);//查找
}
void Union(int x,int y){
int fx=findd(x);
int fy=findd(y);
if(deep[x]<deep[y])
fa[fx]=fy;
else{
if(deep[x]==deep[y]) deep[x]++;
fa[fy]=fx;
}
}
int kruskal(){
int ans=0;
init();
for(int i=1; i<=m; i++){
if(findd(s[i].u) != findd(s[i].v)){
Union(s[i].u,s[i].v);
ans+=s[i].val;
}
}
return ans;
}
int main() {
while(~scanf("%d",&n)){
if(n==0) break;
m = n*(n-1)/2;
for(int i=1; i<=m; i++){
scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].val);
}
sort(s+1,s+1+m,cmp);
int ans=kruskal();
printf("%d\n",ans);
}
return 0;
}