달이 차오른다, 가자.
https://www.acmicpc.net/problem/1194
시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
---|---|---|---|---|---|
2 초 | 128 MB | 13253 | 5293 | 3586 | 37.393% |
문제
지금 민식이가 계획한 여행은 달이 맨 처음 뜨기 시작할 때 부터, 준비했던 여행길이다.
하지만, 매번 달이 차오를 때마다 민식이는 어쩔 수 없는 현실의 벽 앞에서 다짐을 포기하고 말았다.
매번 자신의 다짐을 말하려고 노력했지만, 말을 하면 아무도 못 알아들을 것만 같아서,
지레 겁먹고 벙어리가 되어버렸다.
결국 민식이는 모두 잠든 새벽 네시 반쯤 홀로 일어나, 창 밖에 떠있는 달을 보았다.
하루밖에 남지 않았다. 달은 내일이면 다 차오른다.
이번이 마지막 기회다.
이걸 놓치면 영영 못 간다.
영식이는 민식이가 오늘도 여태 것처럼 그냥 잠들어버려서 못 갈지도 모른다고 생각했다.
하지만 그러기엔 민식이의 눈에는 저기 뜬 달이 너무나 떨렸다.
미로는 직사각형 모양이고, 여행길을 떠나기 위해 미로를 탈출하려고 한다.
미로는 다음과 같이 구성 되어져 있다.
- 빈 칸: 언제나 이동할 수 있다. (‘.’)
- 벽: 절대 이동할 수 없다. (‘#’)
- 열쇠: 언제나 이동할 수 있다. 이 곳에 처음 들어가면 열쇠를 집는다. (‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’)
- 문: 대응하는 열쇠가 있을 때만 이동할 수 있다. (‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’)
- 민식이의 현재 위치: 빈 곳이고, 민식이가 현재 서 있는 곳이다. (‘0’)
- 출구: 달이 차오르기 때문에, 민식이가 가야하는 곳이다. 이 곳에 오면 미로를 탈출한다. (‘1’)
달이 차오르는 기회를 놓치지 않기 위해서, 미로를 탈출하려고 한다.
한 번의 움직임은 현재 위치에서 수평이나 수직으로 한 칸 이동하는 것이다.
민식이가 미로를 탈출하는데 걸리는 이동 횟수의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 미로의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 50)
둘째 줄부터 N개의 줄에 미로의 모양이 주어진다.
같은 타입의 열쇠가 여러 개 있을 수 있고, 문도 마찬가지이다.
그리고, 문에 대응하는 열쇠가 없을 수도 있다.
‘0’은 한 개, ‘1’은 적어도 한 개 있다.
열쇠는 여러 번 사용할 수 있다.
출력
첫째 줄에 민식이가 미로를 탈출하는데 드는 이동 횟수의 최솟값을 출력한다.
만약 민식이가 미로를 탈출 할 수 없으면, -1을 출력한다.
예제 입력 1
1 7 f0.F..1
예제 출력 1
7
예제 입력 2
5 5 ....1 #1### .1.#0 ....A .1.#.
예제 출력 2
-1
예제 입력 3
7 8 a#c#eF.1 .#.#.#.. .#B#D### 0....F.1 C#E#A### .#.#.#.. d#f#bF.1
예제 출력 3
55
예제 입력 4
3 4 1..0 ###. 1...
예제 출력 4
3
예제 입력 5
3 5 ..0.. .###. ..1.A
예제 출력 5
6
예제 입력 6
4 5 0.... .#B#A .#.#. b#a#1
예제 출력 6
19
예제 입력 7
1 11 c.0.C.C.C.1
예제 출력 7
12
예제 입력 8
3 6 ###... #0A.1a ###...
예제 출력 8
-1
출처
- 문제를 번역한 사람: baekjoon
- 문제의 오타를 찾은 사람: jh05013, wkd48632
- 잘못된 조건을 찾은 사람: kesakiyo, ntopia
- 데이터를 추가한 사람: snengggggggg
알고리즘 분류
추가 반례 https://www.acmicpc.net/board/view/23445
예제 입력 A
2 7 1F.f#.0 A...#.a
예제 출력 A
-1
예제 입력 B
2 7 1F.f..0 A.....a
예제 출력 B
6
예제 입력 C
2 7 1F.f#.0 A...#.a
예제 출력 C
-1
예제 입력 D
8 14 1FD...b#....f# AC.....#.....# #.....##.....# #cd....AC....a 1BD....#.....# AC.....AC..... #.....#....... .............0
예제 출력 D
26
예제 입력 E
48 7 1FD.... BC....a #.....# #cd.... .BD.... AC..... #.....# f#...## 1##.### ##....a #.....# #...... ..#b#.. ...#... #.....# ....... #.....# f#...## 1##.### ##....a #.....# #...... ..#b#.. ...#... #.....# ....... .BD.... AC..... #.....# f#...## 1##.### ##....a #.....# #...... ..#b#.. ...#... #.....# ....... #.....# f#...## 1##.### ##....a #.....# #...... ..#b#.. ...#... #.....# ......0
예제 출력 E
67
세로 크기 N과 가로 크기 M (1 ≤ N, M ≤ 50)
소문자 알파벳 열쇠 / 대문자 알파벳 문
1은 적어도 한 개 이상 0은 한 개
탈출이 불가능하다면 -1 출력
BFS로 접근할 생각
입력 확인 코드
#include<iostream> using namespace std; constexpr auto MAX = 51;; int N, M; // 세로 N 가로 M int startX, startY; // 처음 시작위치를 저장하기 위함 char map[MAX][MAX]; void Initalization() { cin >> N >> M; //전부 이동이 불가능한 벽으로 변경 for (int i = 0; i <= N+1; i++) { for (int j = 0; j <= M+1; j++) { map[i][j] = '#'; } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= M ; j++) { cin >> map[i][j]; if (map[i][j] == '0') { startY = i; startX = j; } } } } int main() { Initalization(); cout << startX << " " << startY << " "<< map[0][1]; cout << "\n--------------------------------------------\n"; for (int i = 0; i <= N + 1; i++) { for (int j = 0; j <= M + 1; j++) { cout << map[i][j]; } cout << "\n"; } return 0; }
중간 코드
#include <iostream> #include <queue> #include <algorithm> using namespace std; constexpr auto MAX = 51;; int N, M; // 세로 N 가로 M int startX, startY; // 처음 시작위치를 저장하기 위함 char map[MAX][MAX]; // 하 상 우 좌 int dx[] = { 0, 0, 1, -1 }; int dy[] = { 1, -1, 0, 0 }; typedef struct myStruct { int x, y; bool checkKey[26] = { false }; int cnt = 0; bool checkMap[MAX][MAX] = { false }; }; myStruct tempStruct; queue<myStruct> myQueue; void Initalization() { cin >> N >> M; myStruct tempStruct; //전부 이동이 불가능한 벽으로 변경 for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { map[i][j] = '#'; } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cin >> map[i][j]; if (map[i][j] == '0') { startY = i; startX = j; cout << "(startX, startY) : " << startX << " /" << startY << "\n"; } } } } int BFS() { map[startY][startX] = '.'; myStruct mystruct; mystruct.checkMap[startY][startX] = true; mystruct.x = startX; mystruct.y = startY; myQueue.push(mystruct); cout << myQueue.size() << "\n"; while (!myQueue.empty()) { tempStruct = myQueue.front(); tempStruct.cnt = myQueue.front().cnt + 1; myQueue.pop(); for (int k = 0; k < 4; k++) { cout << "\n-----------------움직임 전---------------------\n"; for (int i = 0; i <= N + 1; i++) { for (int j = 0; j <= M + 1; j++) { cout << map[i][j]; } cout << "\n"; } switch (k) { case 0: cout << "\n하\n"; break; case 1: cout << "\n상\n"; break; case 2: cout << "\n우\n"; break; case 3: cout << "\n좌\n"; break; } mystruct = tempStruct; cout << "mystruct.cnt : "<< mystruct.cnt << "\n"; cout << "이동전의 위치 : mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; // 상하 좌우로 검색 mystruct.x = tempStruct.x + dx[k]; mystruct.y = tempStruct.y + dy[k]; // 맵의 범위를 넘어가거나 // 벽을 만나거나 // if (mystruct.x <= 0 || mystruct.x > M || mystruct.y <= 0 || mystruct.y > N || map[mystruct.y][mystruct.x] == '#' || mystruct.checkMap[mystruct.y][mystruct.x] == true) { cout << "checkMap[mystruct.y][mystruct.x] : " << mystruct.checkMap[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : " << "mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; cout << "맵의 범위를 넘어가거나 // 벽을 만나거나 " << "\n\n"; continue; } else if( 65 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 90 ) { cout << "map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "(int)map[mystruct.y][mystruct.x]] : " << (int)map[mystruct.y][mystruct.x] << "\n"; cout << "(int)map[mystruct.y][mystruct.x]-65] : " << (int)map[mystruct.y][mystruct.x] - 65 << "\n"; // 키가 없으면 넘어간다. if (mystruct.checkKey[(int)map[mystruct.y][mystruct.x]-65] == false) { cout << "// 키가 없으면 넘어간다." << "\n\n"; cout << "이동한 후의 위치 : map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : " << "mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; continue; } else { cout << "// 키가 있으면 빈칸으로 만든다." << "\n\n"; cout << "이동한 후의 위치 : map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : " << "mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; map[mystruct.y][mystruct.x] = '.'; mystruct.checkMap[mystruct.y][mystruct.x] = true; } } else if (97 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 122) { cout << "/키를 줍고 열쇠 위치를 빈칸으로 만들고 전체 방문 체크 초기화" << "\n\n"; cout << "이동한 후의 위치 : map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : " << "mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; mystruct.checkKey[(int)map[mystruct.y][mystruct.x] - 97] = true; map[mystruct.y][mystruct.x] = '.'; for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cout << "초기화"; mystruct.checkMap[i][j] = false; } } } else if (map[mystruct.y][mystruct.x] == '1') { // 미로를 탈출합니다. return mystruct.cnt; } else if (map[mystruct.y][mystruct.x] == '.'|| map[mystruct.y][mystruct.x] == '@') { cout << "빈 공간에 도착" << "\n\n"; cout << "이동한 후의 위치 : map[mystruct.y][mystruct.x] : " << map[mystruct.y][mystruct.x] << "\n"; cout << "이동한 후의 위치 : " << "mystruct.x : " << mystruct.x << " mystruct.y : " << mystruct.y << "\n"; mystruct.checkMap[mystruct.y][mystruct.x] = true; map[mystruct.y][mystruct.x] = '@'; } cout << "\n-----------------움직임 후---------------------\n"; for (int i = 0; i <= N + 1; i++) { for (int j = 0; j <= M + 1; j++) { cout << map[i][j]; } cout << "\n"; } cout << "map[mystruct.y][mystruct.x] : "<< map[mystruct.y][mystruct.x] << "큐에 저장\n"; myQueue.push(mystruct); cout << myQueue.size()<<"\n"; } } // 출구를 찾지 못했을 경우 -1 리턴 return -1; // cout << "A : " << (int)'A' << " a : " << (int)'a' << "\n"; // cout << "B : " << (int)'B' << " b : " << (int)'b' << "\n"; //A : 65 Z : 90 // a : 97 //B: 66 b : 98 } int main() { cout << ". : " << (int)'.' << " . : " << (int)'.' << "\n"; // cout << "a : " << (int)'a' << " z : " << (int)'z' << "\n"; // cout << "B : " << (int)'B' << " b : " << (int)'b' << "\n"; Initalization(); cout << "\n--------------------------------------------\n"; for (int i = 0; i <= N + 1; i++) { for (int j = 0; j <= M + 1; j++) { cout << map[i][j]; } cout << "\n"; } cout << "\n\n 답 : " << BFS() << "\n\n\n"; cout << startX << " " << startY << " "; cout << "\n--------------------------------------------\n"; for (int i = 0; i <= N + 1; i++) { for (int j = 0; j <= M + 1; j++) { cout << map[i][j]; } cout << "\n"; } return 0; }
BFS로 탐색하다
열쇠를 주우면 방문을 초기화
메모리 초과로 고통 받는다.
방문을 체크하는데 및 키를 체크하는데 문제가 있는 듯 하다
메모리 초과 코드 1
#include <iostream> #include <queue> #include <algorithm> using namespace std; constexpr auto MAX = 51; int N, M; // 세로 N 가로 M int startX, startY; // 처음 시작 위치를 저장하기 위함 char map[MAX][MAX]; bool checkMap[MAX][MAX][1 << 6] = {false}; // 하 상 우 좌 int dx[] = { 0, 0, 1, -1 }; int dy[] = { 1, -1, 0, 0 }; typedef struct myStruct { int x, y; int checkKey = 0; int cnt = 0; }; myStruct tempStruct; queue<myStruct> myQueue; void Initalization() { cin >> N >> M; myStruct tempStruct; //전부 이동이 불가능한 벽으로 변경 for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { map[i][j] = '#'; } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cin >> map[i][j]; if (map[i][j] == '0') { startY = i; startX = j; } } } } int BFS() { map[startY][startX] = '.'; myStruct mystruct; mystruct.x = startX; mystruct.y = startY; myQueue.push(mystruct); while (!myQueue.empty()) { tempStruct = myQueue.front(); tempStruct.cnt = myQueue.front().cnt + 1; myQueue.pop(); for (int k = 0; k < 4; k++) { mystruct = tempStruct; mystruct.x = tempStruct.x + dx[k]; mystruct.y = tempStruct.y + dy[k]; if (mystruct.x <= 0 || mystruct.x > M || mystruct.y <= 0 || mystruct.y > N || map[mystruct.y][mystruct.x] == '#')// )|| checkMap[mystruct.y][mystruct.x][mystruct.checkKey] == true) { continue; } else if( 65 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 90 ) { if ((mystruct.checkKey & 1 << ((int)map[mystruct.y][mystruct.x] - 64)) <= 0 ) { continue; } else { } } else if (97 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 122) { mystruct.checkKey = mystruct.checkKey | 1 << ((int)map[mystruct.y][mystruct.x] - 96); } else if (map[mystruct.y][mystruct.x] == '1') { return mystruct.cnt; } else if (map[mystruct.y][mystruct.x] == '.') { } checkMap[mystruct.y][mystruct.x][mystruct.checkKey] = true; myQueue.push(mystruct); } } return -1; } int main() { Initalization(); cout << BFS(); return 0; }
메모리 초과 코드 2
#include <iostream> #include <queue> #include <algorithm> using namespace std; constexpr auto MAX = 51; int N, M; // 세로 N 가로 M int startX, startY; // 처음 시작 위치를 저장하기 위함 char map[MAX][MAX]; bool checkMap[MAX][MAX][1 << 6] = { false }; // 하 상 우 좌 int dx[] = { 0, 0, 1, -1 }; int dy[] = { 1, -1, 0, 0 }; typedef struct myStruct { int x, y; int checkKey = 0; int cnt = 0; }; myStruct tempStruct; queue<myStruct> myQueue; void Initalization() { cin >> N >> M; for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { map[i][j] = '#'; } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cin >> map[i][j]; if (map[i][j] == '0') { startY = i; startX = j; } } } } int BFS() { myStruct mystruct; map[startY][startX] = '.'; mystruct.x = startX; mystruct.y = startY; myQueue.push(mystruct); while (!myQueue.empty()) { tempStruct = myQueue.front(); tempStruct.cnt = myQueue.front().cnt + 1; myQueue.pop(); for (int k = 0; k < 4; k++) { mystruct = tempStruct; mystruct.x = tempStruct.x + dx[k]; mystruct.y = tempStruct.y + dy[k]; if (mystruct.x <= 0 || mystruct.x > M || mystruct.y <= 0 || mystruct.y > N || map[mystruct.y][mystruct.x] == '#' || checkMap[mystruct.y][mystruct.x][tempStruct.checkKey] == true) { continue; } else if (65 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 90) { if ((mystruct.checkKey & 1 << ((int)map[mystruct.y][mystruct.x] - 'A')) > 0) { map[mystruct.y][mystruct.x] = '.'; } else { checkMap[mystruct.y][mystruct.x][mystruct.checkKey] = true; continue; } } else if (97 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 122) { mystruct.checkKey = mystruct.checkKey | 1 << ((int)map[mystruct.y][mystruct.x] - 'a'); } else if (map[mystruct.y][mystruct.x] == '1') { return mystruct.cnt; } else if (map[mystruct.y][mystruct.x] == '.') { checkMap[mystruct.y][mystruct.x][mystruct.checkKey] = true; } myQueue.push(mystruct); } } return -1; } int main() { Initalization(); cout << BFS(); return 0; }
계속 메모리 초과로 인하여 변경한 내용
-> 키의 유무를 비트 마스킹으로 해결
-> 방문 마킹의 위치 변경
통과된 코드
#include <iostream> #include <queue> #include <algorithm> using namespace std; constexpr auto MAX = 51; int N, M; // 세로 N 가로 M int startX, startY; // 처음 시작위치를 저장 char map[MAX][MAX]; // 맵을 저장 bool checkMap[MAX][MAX][1 << 6] = { false }; // 비트 마스킹 // 하 상 우 좌 int dx[] = { 0, 0, 1, -1 }; int dy[] = { 1, -1, 0, 0 }; typedef struct myStruct { int x, y; int checkKey = 0; int cnt = 0; }; myStruct tempStruct; queue<myStruct> myQueue; void Initalization() { cin >> N >> M; for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { map[i][j] = '#'; } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) { cin >> map[i][j]; if (map[i][j] == '0') { startY = i; startX = j; } } } } int BFS() { myStruct mystruct; map[startY][startX] = '.'; mystruct.x = startX; mystruct.y = startY; myQueue.push(mystruct); while (!myQueue.empty()) { tempStruct = myQueue.front(); tempStruct.cnt = myQueue.front().cnt + 1; myQueue.pop(); for (int k = 0; k < 4; k++) { mystruct = tempStruct; mystruct.x = tempStruct.x + dx[k]; mystruct.y = tempStruct.y + dy[k]; // 범위를 벗어나거나 벽이 있을 경우에 넘어간다. if (mystruct.x <= 0 || mystruct.x > M || mystruct.y <= 0 || mystruct.y > N || map[mystruct.y][mystruct.x] == '#' || checkMap[mystruct.y][mystruct.x][tempStruct.checkKey] == true) { continue; } else if (65 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 90) { // 문의 위치일 경우 // 열쇠를 가지고 있다면 if ((mystruct.checkKey & 1 << ((int)map[mystruct.y][mystruct.x] - 'A')) > 0) { checkMap[mystruct.y][mystruct.x][mystruct.checkKey] = true; } else { //열쇠가 없다면 넘어간다. continue; } } else if (97 <= (int)map[mystruct.y][mystruct.x] && (int)map[mystruct.y][mystruct.x] <= 122) { // 열쇠의 위치일 경우 비트 마스킹 mystruct.checkKey = mystruct.checkKey | 1 << ((int)map[mystruct.y][mystruct.x] - 'a'); } else if (map[mystruct.y][mystruct.x] == '.') { // 빈곳일경우 } else if (map[mystruct.y][mystruct.x] == '1') { // 도착지점 return mystruct.cnt; } checkMap[mystruct.y][mystruct.x][mystruct.checkKey] = true; myQueue.push(mystruct); } } return -1; } int main() { Initalization(); cout << BFS(); return 0; }
메모리 초과로 고통받은 문제