백준 16928번 (뱀과 사다리 게임, C++, BFS) [BAEKJOON]

뱀과 사다리 게임

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

시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초512 MB235678517644533.294%

문제

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

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

게임은 정육면체 주사위를 사용하며, 주사위의 각 면에는 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번 칸에 도착할 수 있는 입력만 주어진다.

출력

100번 칸에 도착하기 위해 주사위를 최소 몇 번 굴려야 하는지 출력한다.

예제 입력 1

3 7
32 62
42 68
12 98
95 13
97 25
93 37
79 27
75 19
49 47
67 17
  1. 5를 굴려 6으로 이동한다.
  2. 6을 굴려 12로 이동한다. 이 곳은 98로 이동하는 사다리가 있기 때문에, 98로 이동한다.
  3. 2를 굴려 100으로 이동한다.

예제 출력 1

3

예제 입력 2

4 9
8 52
6 80
26 42
2 72
51 19
39 11
37 29
81 3
59 5
79 23
53 7
43 33
77 21
  1. 5를 굴려 6으로 이동하고, 사다리를 이용해 80으로 이동한다. 
  2. 6을 굴려 86으로
  3. 6을 또 굴려 92로
  4. 6을 또 굴려 98로 이동하고
  5. 2를 굴려 100으로 이동한다.

예제 출력 2

5

출처

  • 문제를 번역한 사람: baekjoon
  • 빠진 조건을 찾은 사람: jh05013

알고리즘 분류


https://kr.123rf.com/photo_27714698_%EB%B1%80%EA%B3%BC-%EC%82%AC%EB%8B%A4%EB%A6%AC-%EB%B3%B4%EB%93%9C-%EA%B2%8C%EC%9E%84.html

접근 방법

너비 우선 검색(BFS)을 사용하여 시작 위치에서 최종 위치까지의 최단 경로를 찾으면 됩니다.

BFS 탐색 시 Queue에는 사각형의 위치와 해당 사각형에 도달하는 데 필요한 주사위 횟수를 저장합니다.

Queue에서 꺼내고 방문한 각 사각형에 대해 주사위를 굴리고 (1 – 6)

주사위 만큼 다음 사각형으로 이동하고 주사위 횟수 + 1 을 해줍니다.

이 과정에서 사다리와 뱀에 대한 구현과 이동할 위치의 최소 주사위 횟수를 이용하여 경우의 수를 줄입니다.

최종 위치에 도달할 때까지 위의 과정을 반복합니다.

BFS 탐색을 어느 정도 알고 있다면 아래의 코드로 풀이를 쉽게 이해 가능하다.

통과된 코드

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

constexpr int MAX = 101;

constexpr int INF = INT32_MAX;

// 사다리와 뱀을 저장한다.
vector<int> graph[MAX];

// BFS 탐색을 위한 Queue
queue<pair<int,int>> myQueue;

// BFS 탐색에서 주사위의 카운트,
// 방문처리 용도로 사용
int map[MAX];

int N, M, tempO, tempT, result;

int main()
{
	ios_base::sync_with_stdio(false); // scanf와 동기화를 비활성화
    //cin.tie(null); 코드는 cin과 cout의 묶음을 풀어줍니다.
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N >> M;

	// 맵을 전부 INF(INT32_MAX)로 변경한다.
	fill(map, map + MAX, INF);

	// 사다리와 뱀의 경로를 넣어준다.
	for (int i = 0; i < N + M; i++) {
		cin >> tempO >> tempT;
		// tempO -> tempT
		graph[tempO].push_back(tempT);
	}

	// 1번에서 시작, 주사위 횟수 0
	myQueue.push(make_pair(1,0));

	while (!myQueue.empty()) {

		int now = myQueue.front().first;
		int cnt = myQueue.front().second;
		myQueue.pop();

		// 결과에 도착했다면 넘어간다.
		if (now == 100) {
			result = cnt;
			break;	
		} 

		for (int i = 1; i <= 6; i++) {

			int tempNow = now + i;

			// 만약 101과 크거나 같다면 넘어간다.
			// 어파치 100에 도착했을때가 큐에 들어가 있어서 
			// 결과에 영향이 없다.
			if (tempNow >= MAX) continue;

			// 해당 칸에 있는 사다리와 뱀을 확인하고 적용한다.
			for (int j = 0; j < graph[tempNow].size(); j++) {
				tempNow = graph[tempNow][j];
			}

			// 해당 칸에 도착할때의 카운트가 기존의 카운트보다 크다면
			// 넘어간다.
			if (map[tempNow] <= cnt + 1) continue;

			map[tempNow] = cnt + 1;
			myQueue.push(make_pair(tempNow, map[tempNow]));
		}
	}

	cout << result;

	return 0;
}

추가 반례

예제 입력 A

2 1
7 94
8 94
55 54

예제 출력 A

2

예제 입력 B

2 1
2 60
30 98
65 25

예제 출력 B

4

예제 입력 C

2 1
2 50
51 100
52 2

예제 출력 C

2

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤