connecting the dots

[BOJ/Java] 17135 : 캐슬 디펜스 본문

algorithm/BOJ

[BOJ/Java] 17135 : 캐슬 디펜스

林 : 2021. 2. 17. 15:00

문제

www.acmicpc.net/problem/17135

 

17135번: 캐슬 디펜스

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 1은 적이 있는 칸이다.

www.acmicpc.net

 

풀이

바로 이전에 풀었던 아기상어 문제와 상당히 비슷했다

    - 처음 설계

고려해야 하는 부분

  • 궁수들은 같은 적을 공격할 수 있다 / 모든 궁수는 동시에 공격한다 - 첫 번째 궁수가 공격할 적을 바로 삭제해버리면 두 번째 궁수가 공격할 적이 바뀔 수도 있다. 따라서 각 궁수가 공격할 적을 모두 정한 다음에 한 번에 공격해야 한다

 

코드

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {// 캐슬 디펜스
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static StringTokenizer st;
	static int[][] dir = { { 0, 1 },  { -1, 0 }, { 0, -1 }, { 1, 0 } };
	static int[][] map;
	static int[][] original;
	static int n, m, d;
	static boolean[] isSelected;
	static int[] attacker = new int[3];
	static boolean[][] visited;
	static Queue<Node> queue = new LinkedList<>();
	static Queue<enemy> enemiesA = new LinkedList<>();//거리 d이하인 적들을 넣어줄 큐
	static Queue<enemy> enemiesB = new LinkedList<>();
	static Queue<enemy> enemiesC = new LinkedList<>();
	static int count, maxCount;

	public static void combination(int cnt, int start) {
		if (cnt == 3) {
			attack(attacker[0], attacker[1], attacker[2]);
			// System.out.println(Arrays.toString(attacker));
			for(int i = 1; i <= n; i++) {
				for(int j = 1; j <= m; j++) {
					map[i][j] = original[i][j];
				}
			}
			
			return;
		}
		for (int i = start; i <= m; i++) {
			attacker[cnt] = i;
			combination(cnt + 1, i + 1);
		}
	}

	public static void attack(int A, int B, int C) {
		// 궁수 위치 설정
		int[] attackerLocation = { A, B, C };
		count = 0;
		while (true) {
			for (int l : attackerLocation) {
				visited = new boolean[n + 2][m + 1];
				queue.offer(new Node(n + 1, l, 0));
				while (!queue.isEmpty()) {
					Node temp = queue.poll();
					int x = temp.x;
					int y = temp.y;
					int dist = temp.dist;
					if (dist >= d)
						continue;
					visited[x][y] = true;
					for (int d = 0; d < 4; d++) {
						int dx = x + dir[d][0];
						int dy = y + dir[d][1];

						if (dx <= 0 || dy <= 0 || dx > n || dy > m)
							continue;
						if (visited[dx][dy])
							continue;
						if (map[dx][dy] == 1) {
							visited[dx][dy] = true;
							if (l == A) {
								enemiesA.offer(new enemy(dx, dy, dist + 1, 0, 0));
							} else if (l == B) {
								enemiesB.offer(new enemy(dx, dy, 0, dist + 1, 0));
							} else if (l == C) {
								enemiesC.offer(new enemy(dx, dy, 0, 0, dist + 1));
							}
							queue.offer(new Node(dx, dy, dist + 1));
						} else {
							visited[dx][dy] = true;
							queue.offer(new Node(dx, dy, dist + 1));
						}
					}
					dist++;
				}

			}
			// 공격할 적 선정하기
			enemy targetA = null, targetB = null, targetC = null;
			if (!enemiesA.isEmpty()) {
				targetA = enemiesA.poll();
				for (enemy e : enemiesA) {//거리가 가장 가까우면서도 왼쪽에 있는 적을 target으로 선정
					if (e.distA <= targetA.distA && e.y < targetA.y) {
						targetA = e;
					}
				}
			}
			if (!enemiesB.isEmpty()) {
				targetB = enemiesB.poll();
				for (enemy e : enemiesB) {
					if (e.distB <= targetB.distB && e.y < targetB.y) {
						targetB = e;
					}
				}
			}
			if (!enemiesC.isEmpty()) {
				targetC = enemiesC.poll();
				for (enemy e : enemiesC) {
					if (e.distC <= targetC.distC && e.y < targetC.y) {
						targetC = e;
					}
				}
			}

			// 공격 후 없애주고 count+1 해주기, 만약 다른 궁수가 이미 공격해서 0이 되었다면 count하지 않는다
			if (targetA != null && map[targetA.x][targetA.y] == 1) {
				map[targetA.x][targetA.y] = 0;
				count++;
			}
			if (targetB != null &&map[targetB.x][targetB.y] == 1) {
				map[targetB.x][targetB.y] = 0;
				count++;
			}
			if (targetC != null && map[targetC.x][targetC.y] == 1) {
				map[targetC.x][targetC.y] = 0;
				count++;
			}

			move();//적이 한 칸씩 내려온다
			
			if (!isEnemy())//적이 모두 사라지면 while문을 빠져나옴
				break;
			
			//적의 정보를 넣은 큐를 clear해주고 적이 움직인 위치에서 다시 while문이 돌아간다
			enemiesA.clear();
			enemiesB.clear();
			enemiesC.clear();
		}

		if (maxCount < count)
			maxCount = count;
		//System.out.println(count);
	}

	public static void move() {//적이 한 칸씩 내려온다
		for (int i = n; i >= 1; i--) {
			for (int j = 1; j <= m; j++) {
				if (map[i][j] == 1) {
					if (i < n) {
						map[i + 1][j] = 1;
						map[i][j] = 0;
					} else if (i == n) {//성이 있는 칸으로 이동한 경우 제외한다
						map[i][j] = 0;
					}
				}
			}
		}
	}

	public static boolean isEnemy() {
		for (int i = n; i >= 1; i--) {
			for (int j = 1; j <= m; j++) {
				if (map[i][j] == 1)
					return true;
			}
		}
		return false;
	}

	public static void main(String[] args) throws Exception {

		st = new StringTokenizer(in.readLine());
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		d = Integer.parseInt(st.nextToken());
		isSelected = new boolean[m + 1];
		map = new int[n + 2][m + 1]; // n+1행에는 궁수가 있다

		for (int i = 1; i <= n; i++) {
			st = new StringTokenizer(in.readLine(), " ");
			for (int j = 1; j <= m; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		
		original= new int[n+2][m+1];
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				original[i][j] = map[i][j];
			}
		}

		combination(0, 1);
		System.out.println(maxCount);


	}

	static class Node {
		int x, y, dist;

		Node(int x, int y, int dist) {
			this.x = x;
			this.y = y;
			this.dist = dist;
		}
	}

	static class enemy {
		int x, y, distA, distB, distC; // 위치와 궁수로부터 떨어진 거리

		enemy(int x, int y, int distA, int distB, int distC) {
			this.x = x;
			this.y = y;
			this.distA = distA;
			this.distB = distB;
			this.distC = distC;
		}

	}

}

 

느낀점

  • 테케는 맞는데 자꾸 틀렸습니다 나왔던 이유가 다 오타때문이었다 ... 조심하자
  • 중복을 최소화하자

'algorithm > BOJ' 카테고리의 다른 글

[BOJ/Java] 15686 : 치킨 배달  (0) 2021.02.19
[BOJ/Java] 3190 : 뱀  (0) 2021.02.19
[BOJ/Java] 16236 : 아기 상어  (0) 2021.02.17
[BOJ/Java] 3055 : 탈출  (0) 2021.02.16
[BOJ/Java] 4963 : 섬의 개수  (0) 2021.02.16