北极通讯网络(kruskal + 贪心)
题目:北极通讯网络
题目链接:https://ac.nowcoder.com/acm/problem/50361
题意:有n个村庄,每个村庄坐标用(x, y)表示,要在村庄间建立通讯网络。有两种通讯工具:
- 每个村庄有一台无线电收发机,无线电收发机的参数d是每两个使用无线电收发机通讯的村庄的坐标的距离的最大值。
- 有k台卫星设备,有卫星设备的村庄两两之间可以通讯。
问怎么分配k台卫星设备,能够使得无线电收发机的参数d最小。
输入描述:
第一行给出村庄个数n(1 <= n <= 500)和卫星设备台数k(0 <= k <= 100)。
接下来n行,每行两个整数x, y(0 <= x, y <= 10000),第i行表示第i个村庄的坐标。
输出描述:
输出最小的d值,保留两位小数。
样例输入:
3 2
10 10
10 0
30 0
样例输出:
10.00
样例解释:
两台卫星设备给第2,3个村庄,村庄2,3使用卫星设备直接通讯。村庄1和村庄2使用无线电收发机通讯,村庄1,2的距离为10,此时d的值为10。
题目分析:最小生成树 + 贪心。可以使用kruskal算法求解。
解题步骤:
- 在每两个村庄间都连接一条边,边权为两个村庄的距离。
- 做kruskal算法,将最小生成树的边权从小到大用数组dis存起来。
- 贪心,长的边所连接的两个村庄用卫星设备直接通讯。有k台卫星设备,当k = 0或k = 1时,通讯设备没办法“抵消”边,即没有两个村庄可以通过卫星设备通讯。此外,k台卫星设备可以抵消k – 1条边,而最小生成树有n – 1条边,那么使用无线电通讯的村庄的最大距离,即d的值为dis[n - k]。
AC代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 510;
struct st{
int x, y;
double z;
bool operator < (const st &X) const{
return z < X.z;
}
}a[N * N];
struct st2{
int x, y;
}c[N];
int n, k, parent[N];
double dis[N];
double Dis(int u, int v){
int dx = c[u].x - c[v].x;
int dy = c[u].y - c[v].y;
return sqrt(dx * dx + dy * dy);
}
int find(int x){
if(x != parent[x]) parent[x] = find(parent[x]);
return parent[x];
}
void Union(int x, int y){
parent[find(x)] = find(y);
}
void solve(){
scanf("%d %d", &n, &k);
for(int i = 1;i <= n;i++){
parent[i] = i;
scanf("%d %d", &c[i].x, &c[i].y);
}
int cnt = 1;
for(int i = 1;i <= n;i++){
for(int j = i + 1;j <= n;j++){
a[cnt++] = {i, j, Dis(i, j)};
}
}
sort(a + 1, a + cnt);
int count = 1;
for(int i = 1;i < cnt;i++){
if(find(a[i].x) != find(a[i].y)){
Union(a[i].x, a[i].y);
dis[count] = a[i].z;
count++;
if(count == n) break;
}
}
printf("%.2lf\n", dis[n - k]);
}
int main(void){
solve();
return 0;
}
时间复杂度:O(n ^ 3)。
空间复杂度:O(n ^ 2)。