#include #include #include #include struct cell; struct group; typedef struct cell cell; typedef struct group group; struct cell { double x, y, z, r; group *group; }; struct group { // 互いに接続しているセルをまとめたもの int number_of_cells; cell *cells[100]; }; void solve(FILE *stream, int number_of_cells); int divide_into_groups(int number_of_cells, cell cells[], group groups[]); void put_connecting_cells_into_group(cell *target, int number_of_cells, cell cells[]); void create_minimum_distance_matrix(int number_of_groups, group groups[], double minimum_distance_matrix[]); double minimum_distance_between_groups(group *group1, group *group2); #define minimum_distance(group1, group2) minimum_distance_matrix[(group1) * number_of_groups + (group2)] double spanning_tree_minimum_cost(int number_of_groups, double minimum_distance_matrix[]); double distance(cell *cell1, cell *cell2); int has_intersection(cell *cell1, cell *cell2); int main() { #ifdef TARGET_RT_MAC_CFM FILE *stream = fopen("input", "r"); #else FILE *stream = stdin; #endif int number_of_cells; for (;;) { fscanf(stream, "%d", &number_of_cells); if (number_of_cells <= 0) break; solve(stream, number_of_cells); } return 0; } void solve(FILE *stream, int number_of_cells) { int index, number_of_groups; cell cells[100]; group groups[100]; double *minimum_distance_matrix = malloc(sizeof(double) * 100 * 100); if (!minimum_distance_matrix) { fprintf(stderr, "*** Out of memory!!\n"); exit(1); } memset(cells, 0, sizeof(cells)); memset(groups, 0, sizeof(groups)); for (index = 0; index < number_of_cells; index++) fscanf(stream, "%lf%lf%lf%lf", &cells[index].x, &cells[index].y, &cells[index].z, &cells[index].r); number_of_groups = divide_into_groups(number_of_cells, cells, groups); create_minimum_distance_matrix(number_of_groups, groups, minimum_distance_matrix); printf("%.3f\n", spanning_tree_minimum_cost(number_of_groups, minimum_distance_matrix)); free(minimum_distance_matrix); } // 接続しているセルをまとめてグループを作る int divide_into_groups(int number_of_cells, cell cells[], group groups[]) { int number_of_groups = 0, index; for (index = 0; index < number_of_cells; index++) { if (!cells[index].group) { cells[index].group = &groups[number_of_groups]; groups[number_of_groups].cells[groups[number_of_groups].number_of_cells++] = &cells[index]; number_of_groups++; put_connecting_cells_into_group(&cells[index], number_of_cells, cells); } } return number_of_groups; } void put_connecting_cells_into_group(cell *target, int number_of_cells, cell cells[]) { int index; group *target_group = target->group; for (index = 0; index < number_of_cells; index++) { if (cells[index].group == target_group) continue; if (has_intersection(target, &cells[index])) { cells[index].group = target_group; target_group->cells[target_group->number_of_cells++] = &cells[index]; put_connecting_cells_into_group(&cells[index], number_of_cells, cells); } } } // 各グループ間の最短距離(つまり、それぞれに属するセルの表面間最短距離の最小値)を求める void create_minimum_distance_matrix(int number_of_groups, group groups[], double minimum_distance_matrix[]) { int index1, index2; for (index1 = 0; index1 < number_of_groups; index1++) { minimum_distance(index1, index1) = 0.0; for (index2 = index1 + 1; index2 < number_of_groups; index2++) { double distance = minimum_distance_between_groups(&groups[index1], &groups[index2]); minimum_distance(index1, index2) = distance; minimum_distance(index2, index1) = distance; } } } double minimum_distance_between_groups(group *group1, group *group2) { double min = 1e100; int index1, index2; for (index1 = 0; index1 < group1->number_of_cells; index1++) { for (index2 = 0; index2 < group2->number_of_cells; index2++) { double tmp = distance(group1->cells[index1], group2->cells[index2]); tmp -= group1->cells[index1]->r + group2->cells[index2]->r; if (tmp < min) min = tmp; } } return min; } // 各グループを頂点とし、辺のコストがグループ間最短距離となっている無向完全グラフについて、 // Prim のアルゴリズムで全域木の最小コストを求める。緑のアルゴリズムの本 P.205 より引用。 double spanning_tree_minimum_cost(int number_of_groups, double minimum_distance_matrix[]) { double minimum_cost[100], total_cost = 0.0, infinity = 1e100; int nearest_group[100], index, index2; for (index = 1; index < number_of_groups; index++) { minimum_cost[index] = minimum_distance(0, index); nearest_group[index] = 0; } for (index = 1; index < number_of_groups; index++) { double min_cost = minimum_cost[1]; int min_index = 1; for (index2 = 2; index2 < number_of_groups; index2++) { if (minimum_cost[index2] < min_cost) { min_cost = minimum_cost[index2]; min_index = index2; } } total_cost += minimum_distance(min_index, nearest_group[min_index]); minimum_cost[min_index] = infinity; for (index2 = 2; index2 < number_of_groups; index2++) { if (minimum_distance(min_index, index2) < minimum_cost[index2] && minimum_cost[index2] < infinity) { minimum_cost[index2] = minimum_distance(min_index, index2); nearest_group[index2] = min_index; } } } return total_cost; } double distance(cell *cell1, cell *cell2) { double dx = cell1->x - cell2->x, dy = cell1->y - cell2->y, dz = cell1->z - cell2->z; return sqrt(dx * dx + dy * dy + dz * dz); } int has_intersection(cell *cell1, cell *cell2) { return (distance(cell1, cell2) - (cell1->r + cell2->r) < 1e-6); }