【练习时间】2020/3/27
【什么是Prim算法?】
最小生成树Prim算法理解
【题目名称】岛国问题
【问题描述】一家互联网服务商(简称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
【样例说明】不用考虑岛屿之间具有相同距离的情况
【解题思路】
首先我们需要建立网络图,我们可以将每一个点都与其他各点建立通路,然后根据这一图找到最小生成树。
创建一个函数用于建立网络图,由于输入数据为坐标点,因此我们可以设置一个结构体,内存放x,y,m,任意两点之间的权为两点之间的距离
struct Island
{
int x;
int y;
int m;
};
Island land[100];
double Graph[50][50];
int n;
double lowcost[50];
double flag_low[50];
//网图初始化
void CreateGraph()
{
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
int x1=land[i].x,y1=land[i].y,x2=land[j].x,y2=land[j].y;
double dis=sqrt((double)((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));
Graph[i][j]=dis;
Graph[j][i]=dis;
}
}
}
此时的main函数是这样滴:
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>land[i].x>>land[i].y>>land[i].m;
}
memset(flag_low,0,sizeof(flag_low));
CreateGraph();
printf("%.2f",AverTime(Prim(0)));
return 0;
}
然后我们创造一个函数利用Prim算法求最小生成树,在这个函数内部书写算法的时候注意,一个岛屿联通时间并不一定是两点之间的距离,比如:
在上面的图中,假设是最终得到了最小生成树,那么4号的建立通信的时间不是3号与4号的距离,当3和4建立完成之后会发现2 和 3 还没有建立完成,所以无法通信。那么4号最终建立通信的时间应该是40,而不是30,因为,**在同一路线上的点的建立通信完成的时间取决于这条路线上建立时间最长的那部分线段。**所以我们需要对最初的Prim算法做一下变动来解决这一问题。
我的思路是:设置一个变量front_ver来记录min_ver上一个点的位置,lowcost记录的从0号到i号最小距离,我们只需要令lowcost[min_ver]=max(lowcost[front_ver],lowcost[min_ver]);
那么lowcost[min_ver]记录的就是这一线路上的当前最大值。
int FindMin()
{
double sum=100000.00;
int i;
int edge;
for(i=0;i<n;i++)
{
if(lowcost[i]<sum&&flag_low[i]==0)
{
edge=i;
sum=lowcost[i];
}
}
return edge;
}
double Prim(int v)
{
int front_ver=0,min_ver;
double tm=0.00;
//初始化
for(int i=0;i<n;i++)
{
lowcost[i]=Graph[v][i];
}
lowcost[v]=0;
for(int i=0;i<n;i++)
{
min_ver=FindMin();
lowcost[min_ver]=max(lowcost[front_ver],lowcost[min_ver]);
front_ver=min_ver;
tm+=lowcost[min_ver]*land[min_ver].m;
flag_low[min_ver]=1;
for(int j=0;j<n;j++)
{
if(j!=min_ver)
lowcost[j]=min(Graph[min_ver][j],lowcost[j]);
}
}
return tm;
}
最后我们设置一个函数用于计算即可
double AverTime(double tm)
{
double sum_m=0.00;
for(int i=0;i<n;i++)
{
sum_m+=land[i].m;
}
return tm/sum_m;
}
【完整源代码如下】
/*Prim算法
7
11 12 2500
14 17 1500
9 9 750
7 15 600
19 16 500
8 18 400
15 21 250
*/
#include <iostream>
#include <cmath>
#include <string.h>
#include <stdio.h>
using namespace std;
struct Island
{
int x;
int y;
int m;
};
Island land[100];
double Graph[50][50];
int n;
double lowcost[50];
double flag_low[50];
//网图初始化
void CreateGraph()
{
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
int x1=land[i].x,y1=land[i].y,x2=land[j].x,y2=land[j].y;
double dis=sqrt((double)((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));
Graph[i][j]=dis;
Graph[j][i]=dis;
}
}
}
int FindMin()
{
double sum=100000.00;
int i;
int edge;
for(i=0;i<n;i++)
{
if(lowcost[i]<sum&&flag_low[i]==0)
{
edge=i;
sum=lowcost[i];
}
}
return edge;
}
double Prim(int v)
{
int front_ver=0,min_ver;
double tm=0.00;
//初始化
for(int i=0;i<n;i++)
{
lowcost[i]=Graph[v][i];
}
lowcost[v]=0;
for(int i=0;i<n;i++)
{
min_ver=FindMin();
lowcost[min_ver]=max(lowcost[front_ver],lowcost[min_ver]);
front_ver=min_ver;
tm+=lowcost[min_ver]*land[min_ver].m;
flag_low[min_ver]=1;
for(int j=0;j<n;j++)
{
if(j!=min_ver)
lowcost[j]=min(Graph[min_ver][j],lowcost[j]);
}
}
return tm;
}
double AverTime(double tm)
{
double sum_m=0.00;
for(int i=0;i<n;i++)
{
sum_m+=land[i].m;
}
return tm/sum_m;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>land[i].x>>land[i].y>>land[i].m;
}
memset(flag_low,0,sizeof(flag_low));
CreateGraph();
printf("%.2f",AverTime(Prim(0)));
return 0;
}
【测试结果】