P1991 无线通讯网 并查集/最小生成树

 P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目其实不难,数据也很水。主要也是套最小生成树的模板,但是这题也算是个变式了,思考和处理边的思路还是很有价值的。

题目说的很复杂,其实就是有p个顶点,每个顶点可以和自身半径D以内的顶点相互链接,同时还可以选出其中的s个顶点对他们标记,标记的两个顶点可以相互链接。求使所有顶点相连的最小半径D是读多少。

思路:我们可以先不管标记的事,就求出要把所有顶点连起来D最小应该是多少,这个很简单每条边的权就是距离,这也就是最小生成树问题,由kruskal我们可以知道最小的D应该是最小生成树的最长边,也就是kruskal算法最后选择的那条边

最小生成树 Kruskal 和 Prim算法及堆优化_Brokenrivers的博客-CSDN博客

但是D还能不能更小呢?是可以的,因为题目给了我们让任意两个顶点相连的能力,也就是可以任意的链接s-1个联通块,所以我们只需要对kruskal算法改进一下,当已经链接的边(联通块个数)达到p-s的时候停止,然后输出最后添加的边就行了。

然后就是这题其实只给了顶点,我们只需要两两算出距离就能得到每一条边了

#include<bits/stdc++.h>
#pragma warning (disable:4996);
#define ll long long 
#define int ll
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N = 2e5 + 10;
int S, P, k, q, T;
int a, b, c;
int pre[N];
void init(int k) {
	for (int j = 1; j <= k; j++) {
		pre[j] = j;
	}
}
inline int fnd(int k) {
	return pre[k] == k ? k : pre[k] = fnd(pre[k]);
}
void join(int a, int b) {
	pre[fnd(a)] = fnd(b);
}
struct node {
	int x, y;
}v[N];
struct edge {
	int from, to;
	double pw;
	int operator<(const edge& a)const {
		return pw < a.pw;
	}
}edg[N];
int esum = 0;//记录边数
double  kruskal() {
	int e= esum;
	double  sum = 0;
	int cnt = 0;
	int ok = 0;
	init(P);
	sort(edg + 1, edg + e + 1);
	for (int j = 1; j <= e; j++) {
		int t1 = fnd(edg[j].from), t2 = fnd(edg[j].to);
		if (t1 != t2) {
			pre[t1] = t2;

			cnt++;
			if (cnt == P -S) {
				int ok = 1;
				sum = edg[j].pw;
				break;
			}
		}
	}
	return sum;
}
double disa(node a, node b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int used[510][510] = { 0 };
signed main() {
	std::ios::sync_with_stdio(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("out.txt", "w", stdout);
#endif
	cin >> S >> P;
	for (int j = 1; j <= P; j++) {
		cin >> v[j].x >> v[j].y;
	}
	
	for (int j = 1; j <= P; j++) {//处理得边
		for (int i = 1; i <= P; i++) {
			if (j != i) {
				if (used[j][i] == 0) {///注意重复边
					edg[++esum].from = j;
					edg[esum].to = i;
					edg[esum].pw = disa(v[j], v[i]);
					used[j][i] = 1;
					used[i][j] = 1;
				}
			}
		}
	}
	printf("%.2lf\n", kruskal());

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值