题意:有若干块牧场,任意两个牧场之间可能有路连接也可能没有。一块“区域”代表一块或者几块牧场的集合,并且从集合中任意一块牧场都能走到任意另一块牧场。“区域”的直径定义为区域中任意两块牧场之间最短路径的最大值。给定两块或者多块区域,如果在属于两个区域的牧场之间添加一条道路,则会将两块区域合并为一个区域,求这个区域的直径的最小值
解题思路:
- 从输入中得到这些牧场的坐标,用node(x, y)表示,将所有node存与数组nodes中。然后继续读入,得到邻接表table[i][j] = k,k=0代表节点i与j之间没有边,k=1代表节点之间有边
- 用Floyd得到节点间最短路径的长度,用distance_table[i][j] = k保存,代表节点i与节点j之间最短路径为k,如果k=MAX(此题中设为200000),则两个节点之间不通,也就是属于两个不同的“区域”
- 通过遍历distance_table[i][j],可以得到节点i到其他节点的最短路径的最大值,保存为max_distance[i]
- 再次遍历distance_table[i][j],对于distance_table[i][j] = MAX的节点(也就是两者不在同一区域),计算连接i与j之后形成的新区域的直径的可能值,也就是max_distance[i] + distance(i, j) + max_distance[j]。在遍历过程中保存这个值的最小值ret。
- 另外的可能值就是max_distance[i]中的值,也就是连接过后区域直径不变。因为连接过后新区域的直径不可能比连接之前的两个区域的直径中任何一个小,所以还要比较max_distance[i]中的最大值(也就是原来的直径)与ret的大小,取最大值即为结果
代码:
/*
ID: zc.rene1
LANG: C
PROG: cowtour
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define MAX 200000
struct node
{
int x, y;
};
struct node nodes[150];
int table[150][150];
int N;
double distance_table[150][150];
double max_distance[150];
void GetInput(FILE *fin)
{
int i, j;
char c;
fscanf(fin, "%d", &N);
for (i=0; i<N; i++)
{
fscanf(fin, "%d %d", &(nodes[i].x), &(nodes[i].y));
}
fscanf(fin, "%c", &c);
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
fscanf(fin, "%c", &c);
if (c == '0')
{
table[i][j] = 0;
}
else
{
table[i][j] = 1;
}
}
fscanf(fin, "%c", &c);
}
}
double GetDistance(int i, int j)
{
double diff_x = (double)(nodes[i].x - nodes[j].x);
double diff_y = (double)(nodes[i].y - nodes[j].y);
return sqrt(diff_x * diff_x + diff_y * diff_y);
}
void Floyd(void)
{
int i, j, k;
double temp;
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
if (table[i][j] == 1)
{
distance_table[i][j] = GetDistance(i, j);
}
else
{
distance_table[i][j] = MAX;
}
}
}
for (k=0; k<N; k++)
{
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
temp = distance_table[i][k] + distance_table[k][j];
if ((i != j) && (temp < distance_table[i][j]))
{
distance_table[i][j] = temp;
}
}
}
}
}
void GetMaxDistance(void)
{
int i, j;
for (i=0; i<N; i++)
{
max_distance[i] = 0.0;
}
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
if ((max_distance[i] < distance_table[i][j]) && distance_table[i][j] != MAX)
{
max_distance[i] = distance_table[i][j];
}
}
}
}
double GetMinDiameter(void)
{
double ret = MAX, temp;
int i, j;
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
if ((i != j) && (distance_table[i][j] == MAX))
{
temp = GetDistance(i, j) + max_distance[i] + max_distance[j];
if (temp < ret)
{
ret = temp;
}
}
}
}
for (i=0; i<N; i++)
{
if (max_distance[i] > ret)
{
ret = max_distance[i];
}
}
return ret;
}
int main(void)
{
FILE *fin, *fout;
double result;
fin = fopen("cowtour.in", "r");
fout = fopen("cowtour.out", "w");
/*get input*/
GetInput(fin);
/*Floyd*/
Floyd();
/*get the max distance of each point*/
GetMaxDistance();
/*get the minimum diameter*/
result = GetMinDiameter();
fprintf(fout, "%lf\n", result);
return 0;
}