题目链接:洛谷P2212[USACO14MAR]Watering the Fields S
最小生成树
Kruskal算法:
算法详情见最小生成树(kruskal算法)
此题其实用kruskal和prim都是可以的,本人认为kruskal更好打一些所以选择了kruskal
我们先来看一下题目:
有n个点,给出每一个点的坐标((x_i),(y_i))
这样我们可以求出每一对点之间的距离:l = ((x_i-x_j)^2) + ((y_i-y_j)^2)
题目又有要求(lgeqslant c)所以我们可以判断一下是否满足要求然后再加入e数组
将e数组排个序,然后跑一遍kruskal,求出最小生成树的总边权
最后输出答案
别忘了特判:选择的边的数量一定要等于n - 1
现在来看一下代码(附赠注释):
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int read() {
int w = 1,res = 0;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') res = res * 10 + ch - '0', ch = getchar();
return w * res;
} //快读没啥好说
int x[2010], y[2010], m, c, n;//x为点的横坐标,y为点的纵坐标,m为边的总数,c,n如题意
long long ans = 0; //答案记录
struct Edge {
int from, to;
long long w;
} e[10000010]; //存储边的信息
int pf(int x) { //平方
return x * x;
}
bool cmp(Edge a, Edge b) { //排序用的比较函数
return a.w < b.w;
}
int pre[2010]; //并查集父亲数组
int find(int k) {//‘查’+路径压缩
return pre[k] == k ? k : pre[k] = find(pre[k]);
}
void kruskal() { //kruskal算法
int edge_num = 0;
sort(e + 1, e + m + 1, cmp); //将边按大小排序
for(int i = 1; i <= m; i ++) {
if(edge_num == n - 1) break; //如果已经有了n-1条边,那么直接退出循环
if(find(e[i].from) != find(e[i].to)) {
pre[find(e[i].to)] = find(e[i].from);//并
ans += e[i].w; //总边权加上那一条边的边权
edge_num ++; //选择的边数要加一
}
}
if(edge_num < n - 1) ans = -1; //如果没有选择够n-1条边,说明这张图还不是树,ans=-1
}
int main() {
n = read(), c = read(), m = 0;
for(int i = 1; i <= n; i ++)
x[i] = read(), y[i] = read(), pre[i] = i;//记得初始化
for(int i = 1; i <= n; i ++)
for(int j = i + 1; j <= n; j ++) {
long long l = pf(x[i] - x[j]) + pf(y[i] - y[j]);//计算边的长度
if(l < c) continue; //如果边的长度小于c就直接continue
e[++ m].from = i;
e[m].to = j;
e[m].w = l; //加边操作
}
if(m < n - 1) { //如果总边数都小于n-1,直接输出-1
cout << -1;
return 0;
}
kruskal();
cout << ans;
return 0;
}