백준

[백준_16928번] 뱀과 사다리 게임

빙수빈수 2021. 10. 22. 19:24

https://www.acmicpc.net/problem/16928

 

16928번: 뱀과 사다리 게임

첫째 줄에 게임판에 있는 사다리의 수 N(1 ≤ N ≤ 15)과 뱀의 수 M(1 ≤ M ≤ 15)이 주어진다. 둘째 줄부터 N개의 줄에는 사다리의 정보를 의미하는 x, y (x < y)가 주어진다. x번 칸에 도착하면, y번 칸으

www.acmicpc.net

[문제]

뱀과 사다리 게임을 즐겨 하는 큐브러버는 어느 날 궁금한 점이 생겼다.

 

"주사위를 조작해 내가 원하는 수가 나오게 만들 수 있다면, 최소 몇 번만에 도착점에 도착할 수 있을까?"

 

 게임은 정육면체 주사위를 사용하며, 주사위의 각 면에는 1부터 6까지 수가 하나씩 적혀있다. 게임은 크기가 10×10이고, 총 100개의 칸으로 나누어져 있는 보드판에서 진행된다. 보드판에는 1부터 100까지 수가 하나씩 순서대로 적혀져 있다.

 플레이어는 주사위를 굴려 나온 수만큼 이동해야 한다. 예를 들어, 플레이어가 i번 칸에 있고, 주사위를 굴려 나온 수가 4라면, i+4번 칸으로 이동해야 한다. 만약 주사위를 굴린 결과가 100번 칸을 넘어간다면 이동할 수 없다. 도착한 칸이 사다리면, 사다리를 타고 위로 올라간다. 뱀이 있는 칸에 도착하면, 뱀을 따라서 내려가게 된다. 즉, 사다리를 이용해 이동한 칸의 번호는 원래 있던 칸의 번호보다 크고, 뱀을 이용해 이동한 칸의 번호는 원래 있던 칸의 번호보다 작아진다.

 게임의 목표는 1번 칸에서 시작해서 100번 칸에 도착하는 것이다. 게임판의 상태가 주어졌을 때, 100번 칸에 도착하기 위해 주사위를 굴려야 하는 횟수의 최솟값을 구해보자.

 

[입력 조건]

  • 첫째 줄에 게임판에 있는 사다리의 수 N(1 ≤ N ≤ 15)과 뱀의 수 M(1 ≤ M ≤ 15)이 주어진다.
  • 둘째 줄부터 N개의 줄에는 사다리의 정보를 의미하는 x, y (x < y)가 주어진다. x번 칸에 도착하면, y번 칸으로 이동한다는 의미이다.
  • 다음 M개의 줄에는 뱀의 정보를 의미하는 u, v (u > v)가 주어진다. u번 칸에 도착하면, v번 칸으로 이동한다는 의미이다.
  • 1번 칸과 100번 칸은 뱀과 사다리의 시작 또는 끝이 아니다. 모든 칸은 최대 하나의 사다리 또는 뱀을 가지고 있으며, 동시에 두 가지를 모두 가지고 있는 경우는 없다. 항상 100번 칸에 도착할 수 있는 입력만 주어진다.

 

[코드]

import java.util.*;

public class BaekJoon_16982 {
	static int n,m;
	static int[] count=new int[101]; // 해당 칸에 도착하기까지 이동한 횟수가 저장된 배열
	static int[] map=new int[101];
	static boolean[] visited=new boolean[101];
	static int[] snake_ladder=new int[101];
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		m=sc.nextInt();
		
		for(int i=0;i<n+m;i++) {
			int start=sc.nextInt();
			int end=sc.nextInt();
			
			/*
			 * 사다리나 뱀이 있는 곳은 따로 출발지를 인덱스로 하고
			 * 도착지를 배열 값으로하는 배열에 저장해둔다.  
			 */
			snake_ladder[start]=end;
		}
		
		bfs(1); // 출발지는 1
	}
	
	static void bfs(int start) {
		Queue<Integer> q=new LinkedList<>();
		visited[start]=true; // 방문처리
		count[start]=0; 
		q.offer(start);

		while(!q.isEmpty()) {
			int now=q.poll();
			
			// 주사위를 굴려 이동할 수 있는(1~6칸 만큼) 곳 모두 탐색
			for(int i=1;i<7;i++) {
				int next=now+i;
				
				// 게임판을 넘어가거나 이미 방문한 곳이라면 최단경로를 구할 수 없기 때문에 무시
				if(next>100||visited[next]) 
					continue;
				
				// 이동한 칸이 도착지라면 이동횟수(next로 이동한 한번도 더해야 함) 출력후 종료
				if(next==100) {
					System.out.println(count[now]+1);
					return;
				}
				
				visited[next]=true; // 방문처리
				
				// 만약 이동한 칸에 뱀 or 사다리가 있다면
				if(snake_ladder[next]!=0) {
					/*
					 * 이동할 곳을 아직 방문하지 않은 경우에만 이동해야 한다.
					 * 만약 이미 방문한 곳이라면 최단거리를 구할 수 없기 때문에 무시 
					 */
					if(!visited[snake_ladder[next]]) {			
						visited[snake_ladder[next]]=true; // 방문처리
						q.offer(snake_ladder[next]); // 큐에 삽입
						count[snake_ladder[next]]=count[now]+1; // 이동횟수 저장
 					}
				}
				// 아무것도 없는 칸이라면
				else {
					q.offer(next);
					count[next]=count[now]+1; // 이동횟수 저장
				}
			}
		}
	}
}

 

[고찰]

 이번 문제는 BFS 알고리즘을 사용하여 각 칸에 도달할 수 있는 최단거리를 갱신하면서 목적지까지 도달하는 문제였다. 이때 뱀과 사다리가 있는 칸은 시작점을 인덱스로 하고, 도착지를 값으로 하는 배열에 따로 저장해두고 이동하는 곳이 뱀과 사다리가 있는 곳인지 체크하는 방식으로 해결해야 한다.

 시작하는 곳인 1부터 주사위를 굴려 나올 수 있는 1~6칸을 더해가면서 갈 수 있는 모든 경우의 수를 탐색한다. 이때, 이동할 칸에 뱀이나 사다리가 있고, 뱀이나 사다리를 타고 이동할 곳이 아직 방문하지 않았다면 해당 칸으로 이동한다. 만약 이미 방문한 곳이라면 최단 거리를 구하는 문제의 목적에 맞지 않기 때문에 무시해야 한다. 우선순위 큐를 사용하기 때문에 어떤 칸을 처음 방문했을때가 최단거리이기 때문이다. 반면에 이동할 곳에 아무것도 없는 칸이라면 이동횟수만 늘린다.

 

이번 문제는 뱀과 사다리가 있는 곳은 따로 배열에 저장해두고 사용하는 것만 캐치한다면 평소 풀어봤던 BFS 문제와 크게 다르지 않아 어렵지 않게 해결할 수 있었다.