最小生成树
算法: Prim算法和Kruskal算法
prim算法:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 110
int map[MAXN][MAXN], lowcost[MAXN];
bool visit[MAXN];
int nodenum, sum;
void prim()
{
int temp, k;
sum = 0;
memset(visit, false, sizeof(visit)); //初始化visit
visit[1] = true;
for(int i = 1; i <= nodenum; ++i) //初始化lowcost[i]
lowcost[i] = map[1][i];
for(int i = 1; i <= nodenum; ++i)//找生成树集合点集相连最小权值的边
{
temp = INF;
for(int j = 1; j <= nodenum; ++j)
if(!visit[j] && temp > lowcost[j])
temp = lowcost[k = j];
if(temp == INF) break;
visit[k] = true; //加入最小生成树集合
sum += temp;//记录权值之和
for(int j = 1; j <= nodenum; ++j) //更新lowcost数组
if(!visit[j] && lowcost[j] > map[k][j])
lowcost[j] = map[k][j];
}
}
int main()
{
int a, b, cost, edgenum;
while(scanf("%d", &nodenum) && nodenum)
{
memset(map, INF, sizeof(map));
edgenum = nodenum * (nodenum - 1) / 2;
for(int i = 1; i <= edgenum; ++i) //输入边的信息
{
scanf("%d%d%d", &a, &b, &cost);
if(cost < map[a][b])
map[a][b] = map[b][a] = cost;
}
prim();
printf("%d\n", sum); //最小生成树权值之和
}
return 0;
}
练习题目:http://acm.hdu.edu.cn/showproblem.php?pid=1863
Kruskal算法:
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
详见博客:http://blog.csdn.net/lminy/article/details/50453675
代码实现:
自己习惯代码:
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <queue>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int pre[3000+10];
int n,m;
struct node
{
int u,v,w;
}p[3000+10];
bool cmp(node x,node y)
{
return x.w<y.w;
}
int Find(int x)
{
if(pre[x]==x)
return x;
return pre[x]=Find(pre[x]);
}
void zuixiaoshu()
{
for(int i=1;i<3000+10;i++)
pre[i]=i;
int sum=0,num=0;
int flag=0;
for(int i=1;i<=m;i++)
{
int x=Find(p[i].u);
int y=Find(p[i].v);
if(x!=y)//判断能不能不形成环
{
sum+=p[i].w;
pre[x]=y;
num++;
}
if(num==n-1)
{
flag=1;
break;
}
}
if(flag==0||m<n-1)
cout<<-1<<endl;
else
cout<<sum<<endl;
}
int main()
{
//int n,m;
cin>>n>>m;//n个点,m条边
for(int i=1;i<=m;i++)
{
//int a,b,c;
cin>>p[i].u>>p[i].v>>p[i].w;
}
sort(p+1,p+1+m,cmp);//给边排序
zuixiaoshu();
return 0;
}
网上代码:
typedef struct
{
char vertex[VertexNum]; //顶点表
int edges[VertexNum][VertexNum]; //邻接矩阵,可看做边表
int n,e; //图中当前的顶点数和边数
}MGraph;
typedef struct node
{
int u; //边的起始顶点
int v; //边的终止顶点
int w; //边的权值
}Edge;
void kruskal(MGraph G)
{
int i,j,u1,v1,sn1,sn2,k;
int vset[VertexNum]; //辅助数组,判定两个顶点是否连通
int E[EdgeNum]; //存放所有的边
k=0; //E数组的下标从0开始
for (i=0;i<G.n;i++)
{
for (j=0;j<G.n;j++)
{
if (G.edges[i][j]!=0 && G.edges[i][j]!=INF)
{
E[k].u=i;
E[k].v=j;
E[k].w=G.edges[i][j];
k++;
}
}
}
heapsort(E,k,sizeof(E[0])); //堆排序,按权值从小到大排列
for (i=0;i<G.n;i++) //初始化辅助数组
{
vset[i]=i;
}
k=1; //生成的边数,最后要刚好为总边数
j=0; //E中的下标
while (k<G.n)
{
sn1=vset[E[j].u];
sn2=vset[E[j].v]; //得到两顶点属于的集合编号
if (sn1!=sn2) //不在同一集合编号内的话,把边加入最小生成树
{
printf("%d ---> %d, %d",E[j].u,E[j].v,E[j].w);
k++;
for (i=0;i<G.n;i++)
{
if (vset[i]==sn2)
{
vset[i]=sn1;
}
}
}
j++;
}
}