这是一道求最小生成树的题目。有kruskal算法和prim算法这两种解决最小生成树问题的算法。题意是说有n个点(2<=n<=1000且点的编号从1开始),m个连通方案(1<=m<=15000),并且每个连通方案都有需要消耗电缆的长度。要求从这些连通方案中选取一部分使得所有点都构成一个网络(点与点之间可以有其他的点互相连通。不要构成环,可用并查集实现)并且使所用方案中的最大消耗电缆长度尽量小,然后输出所用方案中最大消耗电缆长度,使用方案数以及每个方案的点的编号。
接下来讲解题思路,输入n和m后依次输入m个连通方案,把连通方案根据消耗电缆长度也就是边的长度按照升序排序,然后遍历方案,当方案中的两个点没有连通(也就是不属于一个集合),那么执行该方案(合并两点)。最后输出方案中的最大电缆消耗长度(也就是最长边),使用方案数和每个方案连通的两个点。这是标准kruskal算法。
注意实际上POJ上的这道题样例是有错误的,可以无视。另外这道题是特殊评测,所以有多种输出方案。
接下来是我的解题代码,kruskal算法:
#include <stdio.h>
#include <stdlib.h>
#define N 1001
#define M 15001
typedef struct /*定义方案结构体*/
{
int x;
int y;
int len; /*消耗电缆长度*/
int flag; /*标志变量,1代表使用该方案*/
}SIDE;
SIDE side[M];
int bleg[N]; /*存储节点*/
int n, m;
void ReadSort(); /*输入方案并为方案排序*/
int Mycompare(const void *a, const void *b); /*方案的比较函数,qsort函数需要*/
int Find(int x);
void Union(int x, int y);
void Init(); /*初始化*/
int main()
{
int i;
int num = 0; /*记录使用的方案数*/
int len = 0; /*记录方案中消耗的电缆最大长度*/
scanf("%d %d", &n, &m);
ReadSort();
Init();
for (i=0; i<m; i++)
{
if (Find(side[i].x) != Find(side[i].y)) /*如果两点没有连通*/
{
len = side[i].len;
num++;
side[i].flag = 1;
Union(side[i].x, side[i].y);
}
}
printf("%d\n%d\n", len, num);
for (i=0; i<m; i++)
{
if (side[i].flag == 1)
{
printf("%d %d\n", side[i].x, side[i].y);
}
}
return 0;
}
void ReadSort() /*输入与排序*/
{
int i;
for (i=0; i<m; i++)
{
scanf("%d %d %d", &side[i].x, &side[i].y, &side[i].len);
}
qsort(side, m, sizeof(SIDE), Mycompare);
return;
}
int Mycompare(const void *a, const void *b) /*排序比较函数*/
{
SIDE *pa = (SIDE *)a;
SIDE *pb = (SIDE *)b;
if (pa->len != pb->len)
{
return (pa->len - pb->len);
}
else if (pa->x != pb->x)
{
return (pa->x - pb->x);
}
return (pa->y - pb->y);
}
void Init() /*初始化函数*/
{
int i;
for (i=1; i<=n; i++)
{
bleg[i] = i;
}
for (i=0; i<m; i++)
{
side[i].flag = 0;
}
return;
}
int Find(int x) /*查找根节点*/
{
int y = bleg[x];
int z;
while (y != bleg[y])
{
y = bleg[y];
}
while (x != bleg[x]) /*路径压缩*/
{
z = bleg[x];
bleg[x] = y;
x = z;
}
return y;
}
void Union(int x, int y) /*合并操作*/
{
int fx = Find(x);
int fy = Find(y);
bleg[fx] = fy;
return;
}