P1991 无线通讯网 传送门
- 题目描述
- 国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;
-
每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都ᤕ有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器
的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。 -
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。
- 输入输出格式
-
输入格式:
从 wireless.in 中输入数据第 1 行,2 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标(x, y),以 km 为单位。 - 输出格式:
- 输出 wireless.out 中第 1 行,1 个实数 D,表示无线电收发器的最小传输距离,㋮确到小数点后两位。
- 输入输出样例
- 输入样例#1:
-
2 4
0 100
0 300
0 600
150 750
输出样例#1: - 212.13
这道题区别于模板的最小生成树的点有两个
一. 题目给出了坐标的数据,而不是点和边. 那么就需要我们计算坐标之间的距离来计算边的长度.
二. 数据给出s个卫星电话, 是可以把边的距离缩短到0的.
这个就是本题的重点与难点了. 如果没有这个第二点, 我们只需找出最小生成树的n - 1条边即可, 答案就是最大的那条边. 但是有了这s个卫星电话, 简直是破坏生态平衡的存在. 怎么设定这s个电话呢? 难道枚举出每一种组合,然后分别求最小生成树吗?
没那么麻烦. 我们需要利用一棵树的性质
删掉s条边, 一棵树将会变成s + 1个联通块.
就是这个性质, 所以我们可以放肆的删掉最小生成树的n - 1条边里最长的 s - 1条边了. 因为我们有s个卫星电话, 那么我们就保证s个联通块里面分别有一个卫星电话, 就可以维持这s个联通块互相连通. 而这些联通块是具体哪一个结点都是无所谓的.
之所以分成s个联通块, 是因为我们的最大资源就是s个卫星电话, 而我们的目的是找出最小的最大边, 所以当然尽可能地删掉最大的边, 能删多少删多少. 那就删s - 1
条边咧.
so, 在代码上反倒是第一点比较复杂, 而第二点是理解起来比较复杂
且允许我把它叫做去边最小生成树
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
const int maxn = 500 + 5;
const double INF = 1e16;
double G[maxn][maxn];
int s, n;
vector<pair<double, double> > point;
bool vis[maxn] = {};
double dis[maxn] = {};
vector<double> vec;
void Prim()
{
dis[0] = 0;
for (int i = 0; i < n; ++i) {
int u = -1;
double _min = INF;
for (int j = 0; j < n; ++j) {
if (!vis[j] && dis[j] < _min) {
_min = dis[j];
u = j;
}
}
vis[u] = true;
vec.push_back(_min);
for (int j = 0; j < n; ++j) {
if (G[u][j] < dis[j]) {
dis[j] = G[u][j];
}
}
}
}
int main()
{
cin >> s >> n;
for (int i = 0; i < n; ++i) {
dis[i] = 1e16;
for (int j = 0; j < n; ++j) {
G[i][j] = 1e16;
}
}
for (int i = 0; i < n; ++i) {
double x, y;
cin >> x >> y;
point.push_back(make_pair(x, y));
}
for (int i = 0; i < point.size(); ++i) {
for (int j = 0; j < point.size(); ++j) {
if (i == j) continue;
double x1 = point[i].first, y1 = point[i].second;
double x2 = point[j].first, y2 = point[j].second;
G[i][j] = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}
}
Prim();
sort(vec.begin(), vec.end());
for (int i = 0; i < vec.size(); ++i) {
if (i == n - s) printf("%.2f", vec[i]);
}
}
/*
2 4
0 100
0 300
0 600
150 750
*/
/*
两个要点
1.根据点的坐标获取边的长度
2.理解一棵树去掉s条边之后一定变成s+1个联通块. (难点)
*/