WUSTOJ 1346: DARK SOULS(Java)并查集

108 篇文章 0 订阅
80 篇文章 18 订阅

题目链接: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)是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

主要操作:

  1. 初始化:把每个元素的集合初始化为自身
  2. 查找:查找元素所在的集合,即根结点
  3. 合并:将两个元素所在的不同集合合并为一个集合

版权声明

  1. 转载、参考、引用必须在首页添加如下文字:
    [WUSTOJ 1346: DARK SOULS(Java)并查集—wowpH](https://blog.csdn.net/pfdvnah/article/details/94644280)
  2. 代码原创,公开引用不能删除首行注释(作者,版本号,时间等信息);
  3. 如果有疑问欢迎评论区留言,尽量解答;
  4. 如果有错误,还望大侠评论区指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值