题目链接:1346: DARK SOULS
并查集系列:WUSTOJ 1319: 球(Java)并查集
Description
CQ最近在玩一款游戏:DARK SOULS,这是一款以高难度闻名的硬派动作游戏,而CQ就在这虐与被虐的反复循环中获得了极大的快感(咦我好像泄露了什么……)。
CQ自诩核心玩家,但是他又是个很懒的人。作为一款小怪都可以一套秒人的游戏,DARK SOULS采取的是即时存储制,一不小心挂了就要从复活点重新跑尸,不仅麻烦还要倍加小心(打死的小怪都复活了……一旦跑尸路上被杀还会发生很丧心病狂的事情……),因此CQ决定采用S/L大法,每隔一段时间退出游戏备份存档。
现在问题来了!我们将CQ当前正探索的区域理想化为N*N的正方形网格(坐标从1到N),区域中有若干群小怪,CQ每刷完K群小怪或者探索完该区域就会退出游戏备份存档。那么怎么才算一群小怪呢?CQ对一群小怪的定义是:若两只小怪的水平距离和垂直距离均小于等于D,那么这两个小怪就属于同一群。(唔,心情好的时候会刷完整个区域也说不定),则属于同一群小怪。在上述条件下,CQ想知道探索完给定区域究竟需要备份多少次存档。
前面已经说了CQ是个很懒的人,他懒得算每次他探索一个区域需要备份多少次存档,于是这任务就交给你咯……
Input
输入第一行是整数T,代表接下来有T组数据。
每组数据第一行是三个整数N(1 <= N < =100),K(1 <= K <= 20),D(0 <= D <= 10),S(1 <= S <= N * N),分别代表区域边长,每次刷怪群数,给定距离,小怪数目。
接下来S行数据每行有两个整数X(1 <= X <= N),Y(1 <= Y <= N),代表每只小怪的坐标。
Output
输出CQ总共备份了多少次。
Sample Input
2
5 2 1 6
1 1
2 2
2 5
3 4
4 1
4 3
5 2 1 6
1 1
5 5
2 2
4 4
5 1
3 3
Sample Output
2
1
分析?
典型的并查集问题。
通俗来讲,每个小怪有个标记,用father[]
保存,如果某两个小怪是属于一个群的,就将其中一个的标记改为和另外一个的相同。并且在这个过程中,小怪的群数会减少,最后得到最终的群数。
每次刷怪K
群后备份一次,因此计算刷多少次即可。
代码?
/**
* Time 614ms
* @author wowpH
* @version 2.3
* @date 2019年7月4日下午1:18:53
* Environment: Windows 10
* IDE Version: Eclipse 2019-3
* JDK Version: JDK1.8.0_112
*/
import java.util.Scanner;
public class Main {
private int K, D, S; // 刷怪群数,距离,小怪数量
private int groupNum; // 小怪群数
private int[][] monster;// 小怪坐标
private int[] father; // 父结点
public Main() {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt(); // 数据组数
while ((T--) > 0) {
input(sc); // 输入数据
groupNum = S; // 初始为S群
father = new int[S]; // 小怪的父结点,下标从0开始
for (int i = 0; i < S; ++i) {
father[i] = i; // 初始父结点为自己
}
for (int i = 0; i < S; ++i) { // 遍历所有小怪
for (int j = 0; j < i; ++j) { // 遍历已经记录的小怪
if (true == isGroup(i, j)) {// 判断小怪i,j是否是同一群小怪
merge(i, j); // 是同一群小怪,合并
}
}
}
int ans = groupNum / K;
if (0 != groupNum % K) {// 小怪群数不是刷怪群数的整数倍
ans = ans + 1;
}
System.out.println(ans);// 最终备份次数
}
sc.close();
}
private void input(Scanner sc) {// 输入数据
sc.nextInt(); // int N = sc.nextInt();
K = sc.nextInt();
D = sc.nextInt();
S = sc.nextInt();
monster = new int[S][2]; // 小怪的坐标,下标从0开始
for (int i = 0; i < S; ++i) {
monster[i][0] = sc.nextInt();
monster[i][1] = sc.nextInt();
}
}
private boolean isGroup(int a, int b) { // 判断a和b是否是同一群小怪
if (Math.abs(monster[a][0] - monster[b][0]) > D) { // 垂直距离
return false;
}
if (Math.abs(monster[a][1] - monster[b][1]) > D) { // 水平距离
return false;
}
return true; // 是同一群小怪
}
private void merge(int a, int b) { // 合并a和b
int rootA = findRoot(a); // 查找a的根结点
int rootB = findRoot(b); // 查找b的根结点
if (rootA != rootB) { // 根结点不同
groupNum = groupNum - 1; // 合并,群数减1
if (rootA > rootB) {
father[rootA] = rootB; // a的根结点指向b的根结点
} else {
father[rootB] = rootA; // b的根结点指向a的根结点
}
}
}
private int findRoot(int node) { // 查找node的根结点
if (father[node] != node) { // 根结点不是自己
father[node] = findRoot(father[node]); // 更新node的根结点
}
return father[node]; // 返回根结点
}
public static void main(String[] args) {
new Main();
}
}
并查集
并查集(Union Find)是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
主要操作:
- 初始化:把每个元素的集合初始化为自身
- 查找:查找元素所在的集合,即根结点
- 合并:将两个元素所在的不同集合合并为一个集合
版权声明
- 转载、参考、引用必须在首页添加如下文字:
[WUSTOJ 1346: DARK SOULS(Java)并查集—wowpH](https://blog.csdn.net/pfdvnah/article/details/94644280)
- 代码原创,公开引用不能删除首行注释(作者,版本号,时间等信息);
- 如果有疑问欢迎评论区留言,尽量解答;
- 如果有错误,还望大侠评论区指正。