题目: https://vjudge.net/problem/POJ-2485
最小生成树算法(Prim算法讲得好): http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html
这篇Kruskal算法讲得更清楚 : http://blog.csdn.net/luomingjun12315/article/details/47700237
1、Prim算法
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
//prim解
#include<stdio.h>
#include<set>
using namespace std;
const int qwq = 65536+10;
int size ;
const int n = 500+10;
set<int>v;//初始结点集合
set<int>::iterator it;
int rs;
void build(int map[n][n])
{
const int start = 0;
while(!v.empty())
{
it=v.begin();
int k =*it;
int min = qwq;
//找距离v最近的结点
while(it!= v.end())
{
if(min>map[start][*it])
{
min=map[start][*it];
k=*it;
}
it++;
}
// printf("最短的:%d->长度%d\n",k,min);1
if(rs<min)
{
rs=min;
}
int newstart = k;
for(int i =0;i<size;i++)
{
if(map[start][i]>map[k][i])
{
map[start][i]=map[k][i];
}
}
v.erase(k);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&size);
int map[n][n];
// {
// {0,7,qwq,5,qwq,qwq,qwq},
// {7,0,8,9,7,qwq,qwq},
// {qwq,8,0,qwq,5,qwq,qwq},
// {5,9,qwq,0,15,6,qwq},
// {qwq,7,5,15,0,8,9},
// {qwq,qwq,qwq,6,8,0,11},
// {qwq,qwq,qwq,qwq,9,11,0}
// };
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
{
scanf("%d",&map[i][j]);
}
}
rs=-1;
for(int i=1;i<size;i++)
{
v.insert(i);
}
build(map);
printf("%d\n",rs);
}
return 0;
}
2、Kruskal算法描述
Kruskal算法是基于贪心的思想得到的。首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。至于怎么合并到一个集合,那么这里我们就可以用到一个工具——-并查集(不知道的同学请移步:Here)。换而言之,Kruskal算法就是基于并查集的贪心算法。
//kruskal 把边按权值从小到大排序,从小到大 遍历每一条边, 如果边的两个端点不再同一个联通分支中,就连接起来
#include<stdio.h>
#include<algorithm>
#include<memory.h>
using namespace std;
struct Node
{
int from;
int to;
int wei;
};
//int cmp(Node a,Node b)
//{
// return a.wei<b.wei;
//}
int cmp(const void *a,const void *b)
{
Node aa = *(Node*)a;
Node bb = *(Node*)b;
return aa.wei-bb.wei;
}
//-----------并查集部分 -----------
int pre[500+10];
int find(int x)
{
int r= x;
while(pre[r]!=r)
{
r=pre[r];
}
int i=x,j;
while(i!=r)
{
j= pre[i];
pre[i]=r;
i=j;
}
return r;
}
void join(int a,int b)
{
int fa=find(a);
int fb=find(b);
if(fa!=fb)
{
pre[fa]=fb;
}
}
//------------------
const int size = (500+1)*500/2;
int s;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(pre,0,sizeof(pre));
s=0;
Node edge[size];
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
pre[i]=i;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
int a;
scanf("%d",&a);
if(i<j) //输入每一条边的信息
{
edge[s].from=i;
edge[s].to=j;
edge[s].wei=a;
s++;
}
}
}
qsort(edge,s,sizeof(edge[0]),cmp);
// sort(edge,edge+s,cmp);
int rs = -1;
int t=0;
for(int i=0;i<s;i++)
{
int from = edge[i].from;
int to = edge[i].to;
if(find(from)!=find(to))//不在同一个集合,合并
{
join(from,to);
if(rs<edge[i].wei)
{
rs=edge[i].wei;
}
t++;//连接的边数
}
if(t==n-1){//全部的点已经连在一起了
break;
}
}
printf("%d\n",rs);
}
return 0;
}