백준 - 새로운 게임 2 (17837)

아놀드·2021년 9월 6일
0

백준

목록 보기
26/73

1. 문제

문제 링크

설명
재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다.

게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽 4가지 중 하나이다.

턴 한 번은 1번 말부터 K번 말까지 순서대로 이동시키는 것이다. 한 말이 이동할 때 위에 올려져 있는 말도 함께 이동한다. 말의 이동 방향에 있는 칸에 따라서 말의 이동이 다르며 아래와 같다. 턴이 진행되던 중에 말이 4개 이상 쌓이는 순간 게임이 종료된다.

  • A번 말이 이동하려는 칸이
    • 흰색인 경우에는 그 칸으로 이동한다. 이동하려는 칸에 말이 이미 있는 경우에는 가장 위에 A번 말을 올려놓는다.
      • A번 말의 위에 다른 말이 있는 경우에는 A번 말과 위에 있는 모든 말이 이동한다.
      • 예를 들어, A, B, C로 쌓여있고, 이동하려는 칸에 D, E가 있는 경우에는 A번 말이 이동한 후에는 D, E, A, B, C가 된다.
    • 빨간색인 경우에는 이동한 후에 A번 말과 그 위에 있는 모든 말의 쌓여있는 순서를 반대로 바꾼다.
      • A, B, C가 이동하고, 이동하려는 칸에 말이 없는 경우에는 C, B, A가 된다.
      • A, D, F, G가 이동하고, 이동하려는 칸에 말이 E, C, B로 있는 경우에는 E, C, B, G, F, D, A가 된다.
    • 파란색인 경우에는 A번 말의 이동 방향을 반대로 하고 한 칸 이동한다. 방향을 반대로 바꾼 후에 이동하려는 칸이 파란색인 경우에는 이동하지 않고 가만히 있는다.
    • 체스판을 벗어나는 경우에는 파란색과 같은 경우이다.

다음은 크기가 4×4인 체스판 위에 말이 4개 있는 경우이다.

첫 번째 턴은 다음과 같이 진행된다.

두 번째 턴은 다음과 같이 진행된다.

체스판의 크기와 말의 위치, 이동 방향이 모두 주어졌을 때, 게임이 종료되는 턴의 번호를 구해보자.

입력
첫째 줄에 체스판의 크기 N, 말의 개수 K가 주어진다. 둘째 줄부터 N개의 줄에 체스판의 정보가 주어진다. 체스판의 정보는 정수로 이루어져 있고, 각 정수는 칸의 색을 의미한다. 0은 흰색, 1은 빨간색, 2는 파란색이다.

다음 K개의 줄에 말의 정보가 1번 말부터 순서대로 주어진다. 말의 정보는 세 개의 정수로 이루어져 있고, 순서대로 행, 열의 번호, 이동 방향이다. 행과 열의 번호는 1부터 시작하고, 이동 방향은 4보다 작거나 같은 자연수이고 1부터 순서대로 →, ←, ↑, ↓의 의미를 갖는다.

같은 칸에 말이 두 개 이상 있는 경우는 입력으로 주어지지 않는다.

출력
게임이 종료되는 턴의 번호를 출력한다. 그 값이 1,000보다 크거나 절대로 게임이 종료되지 않는 경우에는 -1을 출력한다.

2. 풀이

2-1. 조건

  1. 하얀 칸일 땐 그대로 이동합니다.
  2. 빨간 칸일 땐 뒤집어서 이동합니다.
  3. 파랑 칸이거나, 좌표 밖인 경우 방향을 반대로 꺾되, 같은 상황이면 가만히 있습니다.

2-2. 풀이

구현을 통한 시뮬레이션 문제입니다.
조건이 많을 뿐이지, 까다로운 부분은 없으며 좀 귀찮을 뿐입니다.
역시 하나씩 계획을 세워 풀어봅시다.

계획1 - 보드판을 스택 배열로 선언합니다.

int [] my = {0, 0, -1, 1};
int [] mx = {1, -1, 0, 0};
int [] transDir = {1, 0, 3, 2};
int[][] horses = new int[K][3]; // 말의 정보를 담을 배열
Stack<Integer>[][] stackBoard = new Stack[N][N]; // 말들을 담을 스택 보드판
Stack<Integer> white = new Stack<>(); // 흰 칸으로 이동할 때 쓰이는 임시 스택
board = new int[N][N]; // 보드판

그 외에도 필요한 변수들을 선언했습니다.
현실 세계의 복잡한 문제를 컴퓨터가 이해할 수 있게 데이터화 하는 작업은 매우 중요합니다.
그 중에서 핵심적인 아이디어 부분은 보드판을 스택 배열로 선언하는 부분입니다.
이로서, 빨간 칸으로 이동할 때 스택에서 pop 만 해주면 문제에서 원하는 조건대로 구현이 가능합니다.

계획2 - 문제의 요구 사항대로 게임을 진행합니다.

이 부분은 순수 구현이기 때문에 별 다른 설명이 없습니다.
전체 코드로 확인합니다.

3. 전체 코드

import java.util.Stack;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
	
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
	static int N, K;
	static int[][] board;
	
	// 파랑칸 여부 (좌표 밖이거나, 2일 때)
	static boolean isBlue(int y, int x) {
		return y < 0 || y >= N || x < 0 || x >= N || board[y][x] == 2;
	}
	
	public static void main(String[] args) throws Exception {
		StringTokenizer stk = new StringTokenizer(br.readLine());
		N = Integer.parseInt(stk.nextToken());
		K = Integer.parseInt(stk.nextToken());
		
		int [] my = {0, 0, -1, 1};
		int [] mx = {1, -1, 0, 0};
		int [] transDir = {1, 0, 3, 2};
		int[][] horses = new int[K][3]; // 말의 정보를 담을 배열
		Stack<Integer>[][] stackBoard = new Stack[N][N]; // 말들을 담을 스택 보드판
		Stack<Integer> white = new Stack<>(); // 흰 칸으로 이동할 때 쓰이는 임시 스택
		board = new int[N][N]; // 보드판
		
		for (int i = 0; i < N; i++) {
			 stk = new StringTokenizer(br.readLine());
			
			for (int j = 0; j < N; j++) {
				stackBoard[i][j] = new Stack<>();
				board[i][j] = Integer.parseInt(stk.nextToken());
			}
		}
		
		for (int i = 0; i < K; i++) {
			stk = new StringTokenizer(br.readLine());
			
			int y = Integer.parseInt(stk.nextToken()) - 1;
			int x = Integer.parseInt(stk.nextToken()) - 1;
			int dir = Integer.parseInt(stk.nextToken()) - 1;
			
			horses[i][0] = y;
			horses[i][1] = x;
			horses[i][2] = dir;
			stackBoard[y][x].add(i);
		}
		
		int ans = -1;
		int loop = 0;
		
		a: while (true) {
			loop++;
			
			// 턴 돌리기
			for (int i = 0; i < K; i++) {
				// 현재 말의 정보를 가져온다
				int[] horse = horses[i];
				int y = horse[0];
				int x = horse[1];
				int dir = horse[2];
				
				// 이동하려는 다음 칸
				int ny = y + my[dir];
				int nx = x + mx[dir];
				
				// 파랑칸일 때
				if (isBlue(ny, nx)) {
					// 좌표를 반대로 바꿔준다
					ny = y - my[dir]; 
					nx = x - mx[dir];
					horse[2] = transDir[dir];
					
					// 반대 방향도 파랑칸일 때
					if (isBlue(ny, nx)) continue;
				}
				
				switch (board[ny][nx]) {
				case 0: // 흰색
					while (!stackBoard[y][x].isEmpty()) {
						int piece = stackBoard[y][x].pop();
						white.add(piece);
						if (piece == i) break; // 현재 이동하는 말일 때 탈출
					}
					
					while (!white.isEmpty()) {
						int piece = white.pop();
						
						// 이동하는 말들의 좌표 갱신
						int[] info = horses[piece];
						info[0] = ny;
						info[1] = nx;
						
						stackBoard[ny][nx].add(piece);
					}
					break;
				case 1: // 빨간색
					while (!stackBoard[y][x].isEmpty()) {
						int piece = stackBoard[y][x].pop();
                        
                                                // 이동하는 말들의 좌표 갱신
						int[] info = horses[piece];
						info[0] = ny;
						info[1] = nx;
						
						stackBoard[ny][nx].add(piece);
                        
						if (piece == i) break; // 현재 이동하는 말일 때 탈출
					}
					break;
				}
				
				// 이동 후 말 4개 이상 모였다면 게임 끝
				if (stackBoard[ny][nx].size() >= 4) {
					ans = loop;
					break a;
				}
				
				if (loop > 1000) break a;
			}
		}
		
		bw.write(ans + "");
		bw.close();
	}
}
profile
함수형 프로그래밍, 자바스크립트에 관심이 많습니다.

0개의 댓글