一道求最小生成树的水题,数据十分水,属于稠密图,适合用prim算法,不过本人还是用的kruskal算法(表示目前还不会prim算法)。题意:以一个由七位小写字母组成的字符串为车辆编号,每辆车的编号只能由另一辆车的编号衍生出来(第一辆车不算),衍生的代价为编号中对应位字母不相同的个数。比如aaaaaaa与baaaaaa有一个位的字母不相同,因此它们互相衍生出来的代价是1。又比如在baaaaaa与abaaaaa有两个对应位的字母不同,因此它们互相衍生的代价是2。在样例中,很明显当第二,三,四辆车的编号都由第一辆车编号衍生出来时代价最小,分别为1,1,1。因此总代价为3。题目要求求出这些车辆编号的最小衍生总代价,就是求它们的最小生成树。
题目数据比较水,初步估计没有当N=2000的极限数据。这道题一直错,我查错查了很久,最后发现原来是没有做多组输入中当输入N=0时结束程序的特殊判断(囧),还是不够细心……
下面是我的解题代码
#include <stdio.h>
#include <stdlib.h>
#define N 2010
#define M 10
typedef struct side /*定义边的结构体*/
{
int a, b; /*两点的下标*/
int len; /*边长*/
}side;
side s[N*N];
int bleg[N]; /*并查集使用,存储父节点*/
char type[N][M];
int ans;
int n;
int sn; /*边的数量*/
void Init(); /*初始化*/
void Read(); /*输入点并且计算边*/
int Mycmp(const void *a, const void *b);
void Link(); /*连接边构成最小生成树*/
int Count(int x, int y); /*计算边长*/
int Find(int x); /*查找*/
void Union(int x, int y, int i); /*合并,根据题目增加特殊操作*/
int main()
{
while (~scanf("%d", &n))
{
if (n == 0) break; /*忘了写这个,一直WA(囧)*/
Init();
Read();
qsort(s, sn, sizeof(side), Mycmp);
Link();
}
return 0;
}
void Init() /*初始化*/
{
int i;
ans = 0;
sn = 0;
for (i=0; i<N; i++)
{
bleg[i] = i;
}
return;
}
void Read() /*输入点并且计算边*/
{
int i, j;
for (i=0; i<n; i++)
{
scanf("%s", type[i]);
for (j=0; j<i; j++) /*计算新增加的边*/
{
s[sn].a = i;
s[sn].b = j;
s[sn++].len = Count(i, j);
}
}
return;
}
int Count(int x, int y) /*计算边长*/
{
int i;
int num = 0;
for (i=0; i<7; i++)
{
if (type[x][i] != type[y][i])
{
num++;
}
}
return num;
}
int Mycmp(const void *a, const void *b)
{
return (*(side *)a).len - (*(side *)b).len;
}
void Link() /*连接边构成最小生成树*/
{
int i;
for (i=0; i<sn; i++)
{
Union(s[i].a, s[i].b, i);
}
printf("The highest possible quality is 1/%d.\n", ans);
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 i) /*合并,根据题目增加特殊操作*/
{
int fx = Find(x);
int fy = Find(y);
if (fx == fy) return;
ans += s[i].len; /*计算最小生成树的边长*/
bleg[fx] = fy;
return;
}