Algorithm/백준(BOJ)

[백준] 16197. 두 동전 (Java)

Carroti 2022. 12. 10. 03:27

 

문제

 

16197번: 두 동전

N×M 크기의 보드와 4개의 버튼으로 이루어진 게임이 있다. 보드는 1×1크기의 정사각형 칸으로 나누어져 있고, 각각의 칸은 비어있거나, 벽이다. 두 개의 빈 칸에는 동전이 하나씩 놓여져 있고,

www.acmicpc.net

 

 

알고리즘

깊이우선탐색(DFS), 백트래킹

 

 

풀이

깊이우선탐색을 통해 상하좌우 네 방향으로 동전을 이동시키며 모든 경우를 탐색해 최소 횟수를 찾는다.

 

아이디어는 다음과 같다.

1. 동전의 위치를 보드 배열에 'o' 로 표시하는게 아니라 (y1,x1) (y2,x2) 좌표로 관리한다.

보드 배열을 수정하지 않음으로써 구현을 편하게 할 수 있다.

 

2. [N+2][M+2] 크기의 배열을 선언하여 테두리는 초기화된 0 그대로 두고, 그 안쪽 [N][M] 크기만 입력으로 주어진 보드대로 입력받는다.

board[y1][x1] 혹은 board[y2][x2] 가 0 이라면 각 동전이 보드에서 떨어진 것임을 알 수 있다.

 

3. 버튼을 누른 횟수, 각 동전의 좌표를 매개변수로 깊이우선탐색을 진행한다.

만약 버튼을 누른 횟수가 10을 넘거나, 두 동전이 모두 떨어졌다면 탐색을 중단한다. 

if (count > 10 || (board[y1][x1] == 0 && board[y2][x2] == 0)) return;

 

만약 하나의 동전만 떨어졌다면 최솟값을 갱신하고 탐색을 중단한다.

if ((board[y1][x1] == 0 && board[y2][x2] != 0) || (board[y1][x1] != 0 && board[y2][x2] == 0)) {
	min = Math.min(min, count);
	return;
}

 

만약 하나의 동전도 떨어지지 않았다면 상하좌우로 탐색을 계속 진행한다.

for (int i = 0; i < dy.length; i++) {
	int ny1 = y1 + dy[i];
	int nx1 = x1 + dx[i];
	int ny2 = y2 + dy[i];
	int nx2 = x2 + dx[i];

	if (board[ny1][nx1] == '#') {
		ny1 = y1;
		nx1 = x1;
	}

	if (board[ny2][nx2] == '#') {
		ny2 = y2;
		nx2 = x2;
	}

	dfs(count + 1, ny1, nx1, ny2, nx2);
}

 

4. 탐색을 마친 뒤 최솟값이 한번도 갱신되지 않았다면 -1을 출력하고 그렇지 않다면 갱신된 최솟값을 출력한다.

 

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static int[] dy = { -1, 1, 0, 0 };
	static int[] dx = { 0, 0, -1, 1 };
	static int min;
	static char[][] board;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		int N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());

		board = new char[N + 2][M + 2];
		int[][] coin = new int[2][2];
		int index = 0;

		for (int i = 1; i <= N; i++) {
			String str = br.readLine();
			for (int j = 1; j <= M; j++) {
				if (str.charAt(j - 1) == 'o') {
					coin[index][0] = i;
					coin[index][1] = j;
					index++;
					board[i][j] = '.';
				} else {
					board[i][j] = str.charAt(j - 1);
				}
			}
		}

		min = Integer.MAX_VALUE;
		dfs(0, coin[0][0], coin[0][1], coin[1][0], coin[1][1]);

		System.out.println(min == Integer.MAX_VALUE ? -1 : min);
	}

	private static void dfs(int count, int y1, int x1, int y2, int x2) {

		if (count > 10 || (board[y1][x1] == 0 && board[y2][x2] == 0))
			return;

		if ((board[y1][x1] == 0 && board[y2][x2] != 0) || (board[y1][x1] != 0 && board[y2][x2] == 0)) {
			min = Math.min(min, count);
			return;
		}

		for (int i = 0; i < dy.length; i++) {
			int ny1 = y1 + dy[i];
			int nx1 = x1 + dx[i];
			int ny2 = y2 + dy[i];
			int nx2 = x2 + dx[i];

			if (board[ny1][nx1] == '#') {
				ny1 = y1;
				nx1 = x1;
			}

			if (board[ny2][nx2] == '#') {
				ny2 = y2;
				nx2 = x2;
			}

			dfs(count + 1, ny1, nx1, ny2, nx2);
		}
	}
}