달이 차오른다, 가자.
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;
}

메모리 초과로 고통받은 문제

![백준 10217번 (KCM Travel, C++, Dijkstra) [BAEKJOON]](https://lycos7560.com/wp-content/uploads/2022/10/boj-og-1-2048x1070-1-1024x535.png)
![Programmers 92342 양궁대회 [2022 KAKAO BLIND RECRUITMENT]](https://lycos7560.com/wp-content/uploads/2023/03/programmers.jpg)
![백준 2480번 (주사위 세개, C++) [BAEKJOON]](https://lycos7560.com/wp-content/uploads/boj-og.png)