Kruskal - Arctic Network - POJ 2349
题目:
n 个 村 庄 ( x , y ) , 要 在 两 两 间 建 立 通 讯 。 n个村庄(x,y),要在两两间建立通讯。 n个村庄(x,y),要在两两间建立通讯。
无 限 电 收 发 机 的 参 数 为 d , 可 以 覆 盖 直 线 距 离 在 d 以 内 的 村 庄 。 无限电收发机的参数为d,可以覆盖直线距离在d以内的村庄。 无限电收发机的参数为d,可以覆盖直线距离在d以内的村庄。
卫 星 设 备 可 以 覆 盖 任 意 距 离 的 村 庄 。 卫星设备可以覆盖任意距离的村庄。 卫星设备可以覆盖任意距离的村庄。
有 k 台 卫 星 设 备 , 无 限 电 收 发 机 数 量 不 限 。 有k台卫星设备,无限电收发机数量不限。 有k台卫星设备,无限电收发机数量不限。
要 覆 盖 n 个 村 庄 , 问 如 何 分 配 k 台 卫 星 设 备 , 使 得 使 用 的 无 限 电 收 发 机 的 参 数 d 尽 量 小 , 输 出 最 小 的 参 数 d 。 要覆盖n个村庄,问如何分配k台卫星设备,使得使用的无限电收发机的参数d尽量小,输出最小的参数d。 要覆盖n个村庄,问如何分配k台卫星设备,使得使用的无限电收发机的参数d尽量小,输出最小的参数d。
抽象出题意:
给 定 一 个 n 个 点 的 图 , 两 点 之 间 相 连 通 需 要 一 定 的 花 费 。 给定一个n个点的图,两点之间相连通需要一定的花费。 给定一个n个点的图,两点之间相连通需要一定的花费。
可 从 中 选 择 k 个 点 , 这 k 个 点 无 需 花 费 任 何 代 价 即 可 连 通 。 可从中选择k个点,这k个点无需花费任何代价即可连通。 可从中选择k个点,这k个点无需花费任何代价即可连通。
问 最 少 需 要 多 少 代 价 使 得 整 个 图 连 通 。 问最少需要多少代价使得整个图连通。 问最少需要多少代价使得整个图连通。
两 点 之 间 连 通 的 代 价 为 两 点 间 的 欧 几 里 得 距 离 。 两点之间连通的代价为两点间的欧几里得距离。 两点之间连通的代价为两点间的欧几里得距离。
全 图 的 代 价 为 边 权 的 最 大 值 。 全图的代价为边权的最大值。 全图的代价为边权的最大值。
输入:
首 行 输 入 整 数 T , 共 有 T 组 测 试 数 据 。 首行输入整数T,共有T组测试数据。 首行输入整数T,共有T组测试数据。
每 组 数 据 首 行 包 括 整 数 k 和 n 。 每组数据首行包括整数k和n。 每组数据首行包括整数k和n。
接 着 输 入 n 行 , 表 示 n 个 点 的 坐 标 。 接着输入n行,表示n个点的坐标。 接着输入n行,表示n个点的坐标。
输出:
一 个 整 数 , 表 示 最 小 花 费 。 一个整数,表示最小花费。 一个整数,表示最小花费。
Sample Input
1
2 4
0 100
0 300
0 600
150 750
Sample Output
212.13
数据范围:
1 ≤ k ≤ 100 , k < n ≤ 500 , 0 ≤ x , y ≤ 10000 1≤k≤100,k<n≤500,0≤x,y≤10000 1≤k≤100,k<n≤500,0≤x,y≤10000
分析:
我 们 的 首 要 任 务 是 使 得 n 个 点 连 通 , 在 这 个 前 提 下 , 要 使 得 所 有 边 的 边 权 最 大 值 最 小 。 我们的首要任务是使得n个点连通,在这个前提下,要使得所有边的边权最大值最小。 我们的首要任务是使得n个点连通,在这个前提下,要使得所有边的边权最大值最小。
采 用 k r u s k a l 算 法 , 每 次 添 加 一 条 边 必 然 使 得 图 中 的 连 通 块 数 量 减 少 1 。 采用kruskal算法,每次添加一条边必然使得图中的连通块数量减少1。 采用kruskal算法,每次添加一条边必然使得图中的连通块数量减少1。
通 过 k r u s k a l 算 法 , 优 先 考 虑 边 权 小 的 边 , 直 到 连 通 块 的 数 量 减 少 到 k 个 , 通过kruskal算法,优先考虑边权小的边,直到连通块的数量减少到k个, 通过kruskal算法,优先考虑边权小的边,直到连通块的数量减少到k个,
最 后 对 k 个 连 通 块 , 每 个 连 通 块 中 放 一 个 卫 星 设 备 , 就 能 够 使 得 全 图 连 通 。 最后对k个连通块,每个连通块中放一个卫星设备,就能够使得全图连通。 最后对k个连通块,每个连通块中放一个卫星设备,就能够使得全图连通。
由 于 边 权 是 从 小 到 大 排 序 的 , 最 后 添 加 的 一 条 边 就 是 整 个 图 中 最 长 的 边 , 即 所 求 最 小 的 参 数 d 。 由于边权是从小到大排序的,最后添加的一条边就是整个图中最长的边,即所求最小的参数d。 由于边权是从小到大排序的,最后添加的一条边就是整个图中最长的边,即所求最小的参数d。
说明:
在 k r u s k a l 算 法 执 行 的 过 程 中 : 在kruskal算法执行的过程中: 在kruskal算法执行的过程中:
若 从 小 到 大 依 次 添 加 了 前 n − k 小 的 边 , 形 成 一 整 个 连 通 块 和 k 个 孤 立 的 单 点 , 此 时 必 是 最 优 解 。 若从小到大依次添加了前n-k小的边,形成一整个连通块和k个孤立的单点,此时必是最优解。 若从小到大依次添加了前n−k小的边,形成一整个连通块和k个孤立的单点,此时必是最优解。
若 在 考 虑 某 条 较 小 边 时 生 成 了 环 , 如 果 选 了 这 条 边 , 对 整 个 图 的 连 通 性 无 影 响 , 所 以 不 选 , 放 卫 星 , 是 最 优 解 。 若在考虑某条较小边时生成了环,如果选了这条边,对整个图的连通性无影响,所以不选,放卫星,是最优解。 若在考虑某条较小边时生成了环,如果选了这条边,对整个图的连通性无影响,所以不选,放卫星,是最优解。
所 以 k r u s k a l 是 正 确 的 。 所以kruskal是正确的。 所以kruskal是正确的。
注意:
O
J
上
选
择
G
+
+
与
C
+
+
提
交
的
注
意
点
:
OJ上选择G++与C++提交的注意点:
OJ上选择G++与C++提交的注意点:
参考:G++与C++的区别。
另 外 , 本 题 是 多 组 数 据 , 并 查 集 在 使 用 时 , 点 的 编 号 是 从 0 开 始 的 , 所 以 要 初 始 化 p [ 0 ] = 0 。 另外,本题是多组数据,并查集在使用时,点的编号是从0开始的,所以要初始化p[0]=0。 另外,本题是多组数据,并查集在使用时,点的编号是从0开始的,所以要初始化p[0]=0。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define P pair<int,int>
#define x first
#define y second
using namespace std;
const int N=510, M=N*N;
int n,m,k,T;
int p[N];
P V[N];
double res;
struct edge
{
int u,v;
double w;
bool operator < (const edge &t) const
{
return w<t.w;
}
}E[M];
int Find(int x)
{
if(p[x]!=x) return p[x]=Find(p[x]);
return p[x];
}
double get_dist(P a,P b)
{
double dx=a.x-b.x,dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
void kruskal()
{
sort(E,E+m);
int cnt=n;
for(int i=0;i<m;i++) //n-k
{
if(cnt<=k) break;
int a=Find(E[i].u),b=Find(E[i].v);
if(a!=b)
{
p[a]=b;
res=E[i].w;
cnt--;
}
}
}
int main()
{
cin>>T;
while(T--)
{
m=0;
cin>>k>>n;
for(int i=0;i<=n;i++) p[i]=i; //下面坐标从0开始,所以p[0]也要初始化!
for(int i=0;i<n;i++) cin>>V[i].x>>V[i].y;
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
E[m++]={i,j,get_dist(V[i],V[j])};
kruskal();
printf("%.2f\n",res);
}
return 0;
}