并查集 - Kruskal
第一次敲并查集,裸题,很好过。
题意:有n个城市,每个城市有它的坐标,连接这n个城市所需的线路长度的最小值。
分析:在本题中,任意两个点之间都有边连通,其权值是两城市之间的距离,将边存入edge[]数组里,用Kruskal求解。
AC代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define MAXN 105
#define MAXM 5000
struct Edge //边
{
int u, v;
double w;
}e[MAXM];
int p[MAXM]; //p[i]为节点i在集合中对应的树中的根节点
double x[MAXN], y[MAXN]; //城市坐标
int n, m; //点,边个数
double sum; //权值和
int Find(int x) //找x所在集合的根节点
{
int s;
for(s=x; p[s]>=0; s=p[s])
while(s!=x)
{
int tmp = p[x];
p[x] = s;
x = tmp;
}
return s;
}
void Union(int R1, int R2) //合并
{
int r1 = Find(R1);
int r2 = Find(R2);
int tmp = p[r1] + p[r2];
if(p[r1] > p[r2])
{
p[r1] = r2;
p[r2] = tmp;
}
else
{
p[r2] = r1;
p[r1] = tmp;
}
}
int cmp(const struct Edge *a, const struct Edge *b)
{
if(a->w > b->w) return 1;
else return -1;
}
void Kruskal()
{
int num = 0;
int u, v, i;
memset(p, -1, sizeof(p));
for(i=0; i<m; i++)
{
u = e[i].u;
v = e[i].v;
if(Find(u) != Find(v))
{
sum += e[i].w;
num++;
Union(u, v);
}
}
}
int main()
{
int t = 1;
while(scanf("%d",&n),n)
{
int i,j;
double d;
for(i=0; i<n; i++)
{
scanf("%lf%lf",&x[i],&y[i]);
}
int mm = 0;
for(i=0; i<n; i++) //更新edge数组
for(j=i+1; j<n; j++)
{
d = sqrt( (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]) );
e[mm].u = i;
e[mm].v = j;
e[mm].w = d;
mm++;
}
m = mm;
qsort(e, m, sizeof(e[0]), cmp); //边的权值按从小到大排列
sum = 0.0;
Kruskal();
if(t>1) printf("\n");
printf("Case #%d:\n", t);
printf("The minimal distance is: %.2lf\n", sum);
t++;
}
return 0;
}