【问题描述】一家互联网服务商(简称PIN)在太平洋上发现了几座新岛屿,其中最大的一个岛(称为主岛)已经连接到Internet,但是其他岛和主岛之间没有光缆连接,所以无法上网。为了让所有岛上的居民都能上网,每个岛和主岛之间都必须有直接或者间接的光缆连接。
每条实线表示一根光缆,它的长度等于两个岛屿中心位置(路由器位置)的几何距离,图上的数字是该岛上的居民数量。为了节省成本,所有光缆的总长度尽量小。
岛屿之间的光缆同时开始铺设,以每天一公里的速度铺设,那么较短的路径肯定先铺设完,某岛屿只要与主岛之间存在一条已经铺设好的电缆,就能接入互联网。PIN非常关注所有居民的平均接入互联网的时间,假设第 i 个岛屿的居民人数是mi ,接入互联网需要的时间是ti ,则平均接入时间是:
【输入形式】输入内容包括所有岛屿的信息。第一行是岛屿的个数n(n< 50);接下来共有n行,每一行包括三个正整数:xi yi mi ,中间空格隔开,(xi , yi )是岛屿的位置坐标,坐标轴单位是公里,mi 是该岛屿的居民人数。输入的第一个岛屿是主岛。
【输出形式】输出所有居民的平均接入互联网的平均天数,结果保留两位小数。
【样例输入】
7
11 12 2500
14 17 1500
9 9 750
7 15 600
19 16 500
8 18 400
15 21 250
【样例输出】3.20
【样例说明】不用考虑岛屿之间具有相同距离的情况
【评分标准】该题目由2002年的ACM/ICP World Final改编而成,作为选做题
分析:
本题可用之前分析的Prim最小生成树来生成路径,之后的关键就是在找到这些边的时候记录下这些边,并算出需要等待的时间。
需要等待的时间——
若岛A直接连接到主岛,则A岛需要等待的时间就是二者的距离
假设A通过B连接到主岛,
如果A到B的边长小于B到主岛的边长,则A岛等的时间实际上是B到主岛的时间
否则如果A到B的边长大于B到主岛的边长,则A岛等的时间是A岛修光缆到B岛的时间
#include<stdio.h>
#include<string.h>
#include<math.h>
#define INF 0xfffffff // infinity
#define MAXSIZE 20
int n;
int people[MAXSIZE], point[MAXSIZE][2]; //point[]记录点的坐标位置
double edge[MAXSIZE][MAXSIZE], time[MAXSIZE]; //因为每天铺一公里,所以time[]其实就是距离
void Prim(double edge[][MAXSIZE], int u);
double distance(int a, int b, int c, int d);
int main()
{
int i, j;
int sum_people = 0;
double sum_time = 0;
memset(edge, 0, sizeof(edge)); // void * memset (void * p,int c,size_t n)
scanf("%d", &n);
for(i = 1; i <= n; i++) //读点的坐标和人数
scanf("%d%d%d", &point[i][0], &point[i][1], &people[i]);
for(i = 1; i <= n; i++) //计算总人数
sum_people += people[i];
for(i = 1; i <= n; i++) // 构造邻接矩阵
for(j = 1; j <= n; j++)
edge[i][j] = edge[j][i] = distance(point[i][0], point[i][1], point[j][0], point[j][1]);
Prim(edge, 1); //Prim算法,从主岛(顶点1)开始
for(i=2; i<=n; i++) //计算所有人等待的总时间(主岛除外),故i=2
sum_time += time[i] * people[i];
printf("%.2f\n", sum_time / sum_people);
return 0;
}
void Prim(double edge[][MAXSIZE], int u)
{
int nearvex[MAXSIZE] = {0};
double lowcost[MAXSIZE] = {0}, mincost = INF * 1.0;
int i, j;
int v = -1;
double weight = 0;
for(i=1; i<=n; i++) //NO.1 对距离进行初始化
{
lowcost[i] = edge[u][i];
nearvex[i] = u;
}
lowcost[u] = 0;
nearvex[u] = -1;
for(i=1; i<n; i++) //NO.4 循环n-1次,加入n-1个顶点
{
mincost = INF; // very important!!!!! EVERY TIME mincost should be initialized to find the minimum
for(j=1; j<=n; j++) //NO.3 更新距离,找到边的权值最小的那个点
if(nearvex[j] != -1 && lowcost[j] < mincost)
{
mincost = lowcost[j];
v = j; //记录下这个边的权值最小的点
}
if(v != -1) //如果找到那个点,进行NO.2
{
// printf("%d--(%f)-->%d\n", nearvex[v], lowcost[v], v); //打印加入的流程
//****************************************************************************************************************
//相对于最小生成树的关键代码:
if(nearvex[v] == u) //若毗邻主岛,直接将时间(距离)更新为边长
time[v] = lowcost[v];
else if(lowcost[v] <= time[ nearvex[v] ])
time[v] = time[ nearvex[v] ];
else
time[v] = lowcost[v];
//假设A通过B连接到主岛,如果A到B的边长小于B到主岛的边长,则A岛等的时间实际上是B到主岛的时间
//否则如果A到B的边长大于B到主岛的边长,则A岛等的时间是A岛修光缆到B岛的时间
//****************************************************************************************************************
nearvex[v] = -1; //将点加入T2
weight += lowcost[v];
for(j=1; j<=n; j++) //加入后,对距离进行更新
{
if(nearvex[j] != -1 && edge[v][j] < lowcost[j])
{
lowcost[j] = edge[v][j];
nearvex[j] = v;
}
}
}
}
}
double distance(int a, int b, int c, int d)
{
return sqrt( (a-c)*(a-c) + (b-d)*(b-d) );
}