Skip to content

Commit 05abd78

Browse files
committed
[level 3] Title: [PCCP 기출문제] 4번 / 수레 움직이기, Time: 3613.99 ms, Memory: 4.21 MB -BaekjoonHub
1 parent 991b2e6 commit 05abd78

2 files changed

Lines changed: 283 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# [level 3] [PCCP 기출문제] 4번 / 수레 움직이기 - 250134
2+
3+
[문제 링크](https://school.programmers.co.kr/learn/courses/30/lessons/250134)
4+
5+
### 성능 요약
6+
7+
메모리: 4.21 MB, 시간: 3613.99 ms
8+
9+
### 구분
10+
11+
코딩테스트 연습 > PCCP 기출문제
12+
13+
### 채점결과
14+
15+
정확성: 100.0<br/>합계: 100.0 / 100.0
16+
17+
### 제출 일자
18+
19+
2025년 09월 24일 17:28:58
20+
21+
### 문제 설명
22+
23+
<p><code>n</code> x <code>m</code> 크기 격자 모양의 퍼즐판이 주어집니다.</p>
24+
25+
<p>퍼즐판에는 빨간색 수레와 파란색 수레가 하나씩 존재합니다. 각 수레들은 자신의 시작 칸에서부터 자신의 도착 칸까지 이동해야 합니다.<br>
26+
모든 수레들을 각자의 도착 칸으로 이동시키면 퍼즐을 풀 수 있습니다.</p>
27+
28+
<p>당신은 각 턴마다 <strong>반드시 모든 수레를 상하좌우로 인접한 칸 중 한 칸으로 움직여야 합니다.</strong> 단, 수레를 움직일 때는 아래와 같은 규칙이 있습니다.</p>
29+
30+
<ul>
31+
<li>수레는 벽이나 격자 판 밖으로 움직일 수 없습니다.</li>
32+
<li>수레는 자신이 방문했던 칸으로 움직일 수 없습니다.</li>
33+
<li>자신의 도착 칸에 위치한 수레는 <strong>움직이지 않습니다.</strong> 계속 해당 칸에 고정해 놓아야 합니다.</li>
34+
<li>동시에 두 수레를 같은 칸으로 움직일 수 없습니다.</li>
35+
<li>수레끼리 자리를 바꾸며 움직일 수 없습니다.</li>
36+
</ul>
37+
38+
<p>예를 들어, 아래 그림처럼 <code>n</code> = 3, <code>m</code> = 2인 퍼즐판이 있습니다.</p>
39+
40+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/2d21a258-144f-4d03-81c1-1a857a942efa/rb_horse1.jpg" title="" alt="rb_horse1.jpg"></p>
41+
42+
<ul>
43+
<li>속이 빨간색인 원은 빨간색 수레를 나타냅니다.</li>
44+
<li>속이 파란색인 원은 파란색 수레를 나타냅니다.</li>
45+
<li>테두리가 빨간색인 원은 빨간색 수레의 도착 칸을 나타냅니다.</li>
46+
<li>테두리가 파란색인 원은 파란색 수레의 도착 칸을 나타냅니다.</li>
47+
</ul>
48+
49+
<p>위 퍼즐판은 아래와 같은 순서로 3턴만에 풀 수 있습니다.</p>
50+
51+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/e1c81aa3-238b-4f0e-b21d-697903543b72/rb_horse2.jpg" title="" alt="rb_horse2.jpg"></p>
52+
53+
<ul>
54+
<li>빨간색 사선이 처진 칸은 빨간색 수레가 방문했던 칸을 나타냅니다. 규칙에 따라 빨간색 수레는 빨간색 사선이 처진 칸(방문했던 칸)으로는 이동할 수 없습니다.</li>
55+
<li>파란색 사선이 처진 칸은 파란색 수레가 방문했던 칸을 나타냅니다. 규칙에 따라 파란색 수레는 파란색 사선이 처진 칸(방문했던 칸)으로는 이동할 수 없습니다.</li>
56+
</ul>
57+
58+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/2b78f38c-121a-441c-90f9-704eb0642e96/rb_horse3.jpg" title="" alt="rb_horse3.jpg"></p>
59+
60+
<ul>
61+
<li>위처럼 동시에 수레를 같은 칸으로 움직일 수는 없습니다.</li>
62+
</ul>
63+
64+
<p>퍼즐판의 정보를 나타내는 2차원 정수 배열 <code>maze</code>가 매개변수로 주어집니다. 퍼즐을 푸는데 필요한 턴의 최솟값을 return 하도록 solution 함수를 완성해 주세요. 퍼즐을 풀 수 없는 경우 0을 return 해주세요.</p>
65+
66+
<hr>
67+
68+
<h5>제한사항</h5>
69+
70+
<ul>
71+
<li>1 ≤ <code>maze</code>의 길이 (= 세로 길이) ≤ 4
72+
73+
<ul>
74+
<li>1 ≤ <code>maze[i]</code>의 길이 (= 가로 길이) ≤ 4</li>
75+
<li><code>maze[i][j]</code>는 0,1,2,3,4,5 중 하나의 값을 갖습니다.</li>
76+
</ul></li>
77+
</ul>
78+
<table class="table">
79+
<thead><tr>
80+
<th><code>maze[i][j]</code></th>
81+
<th>의미</th>
82+
</tr>
83+
</thead>
84+
<tbody><tr>
85+
<td>0</td>
86+
<td>빈칸</td>
87+
</tr>
88+
<tr>
89+
<td>1</td>
90+
<td>빨간 수레의 시작 칸</td>
91+
</tr>
92+
<tr>
93+
<td>2</td>
94+
<td>파란 수레의 시작 칸</td>
95+
</tr>
96+
<tr>
97+
<td>3</td>
98+
<td>빨간 수레의 도착 칸</td>
99+
</tr>
100+
<tr>
101+
<td>4</td>
102+
<td>파란 수레의 도착 칸</td>
103+
</tr>
104+
<tr>
105+
<td>5</td>
106+
<td>벽</td>
107+
</tr>
108+
</tbody>
109+
</table>
110+
<ul>
111+
<li>빨간 수레의 시작 칸, 빨간 수레의 도착 칸, 파란 수레의 시작 칸, 파란 수레의 도착 칸은 퍼즐판에 1개씩 존재합니다.</li>
112+
</ul>
113+
114+
<hr>
115+
116+
<h5>입출력 예</h5>
117+
<table class="table">
118+
<thead><tr>
119+
<th>maze</th>
120+
<th>result</th>
121+
</tr>
122+
</thead>
123+
<tbody><tr>
124+
<td>[[1, 4], [0, 0], [2, 3]]</td>
125+
<td>3</td>
126+
</tr>
127+
<tr>
128+
<td>[[1, 0, 2], [0, 0, 0], [5, 0 ,5], [4, 0, 3]]</td>
129+
<td>7</td>
130+
</tr>
131+
<tr>
132+
<td>[[1, 5], [2, 5], [4, 5], [3, 5]]</td>
133+
<td>0</td>
134+
</tr>
135+
<tr>
136+
<td>[[4, 1, 2, 3]]</td>
137+
<td>0</td>
138+
</tr>
139+
</tbody>
140+
</table>
141+
<hr>
142+
143+
<h5>입출력 예 설명</h5>
144+
145+
<p><strong>입출력 예 #1</strong></p>
146+
147+
<p>문제 예시와 같습니다.</p>
148+
149+
<p><strong>입출력 예 #2</strong></p>
150+
151+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/54629429-3bec-4288-a7b4-6303c0929880/rb_horse4.jpg" title="" alt="rb_horse4.jpg"></p>
152+
153+
<p>7턴만에 퍼즐을 풀 수 있습니다. 다른 방법으로도 퍼즐을 풀 수 있지만 7턴보다 빠르게 풀 수는 없습니다.</p>
154+
155+
<p><strong>입출력 예 #3</strong></p>
156+
157+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/c6aed2ad-dbbf-477e-bac7-fd5cd44bad00/rb_horse5.jpg" title="" alt="rb_horse5.jpg"></p>
158+
159+
<p>다음 턴에 파란색 수레가 파란색 수레의 도착 칸에 위치한 후 고정되어 빨간색 수레가 빨간색 수레의 도착 칸에 도착할 수 없게 됩니다.<br>
160+
퍼즐을 풀 수 없으므로 0을 return 해야 합니다.</p>
161+
162+
<p><strong>입출력 예 #4</strong></p>
163+
164+
<p><img src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/0ff7e955-77d6-4760-9e16-75cf2313fc0d/rb_horse6.jpg" title="" alt="rb_horse6.jpg"></p>
165+
166+
<p>수레는 서로 위치를 바꾸면서 움직일 수 없으므로 퍼즐을 풀 수 없습니다. 따라서 0을 return 해야 합니다.</p>
167+
168+
169+
> 출처: 프로그래머스 코딩 테스트 연습, https://school.programmers.co.kr/learn/challenges
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include <bits/stdc++.h>
2+
using namespace std;
3+
4+
const int INF = 1e9;
5+
int dx[4] = {-1,1,0,0};
6+
int dy[4] = {0,0,-1,1};
7+
8+
struct P { int x,y; };
9+
10+
int H, W;
11+
int mp[4][4];
12+
bool visitedPos[4][4][2]; // [x][y][0:red,1:blue]
13+
bool redAtGoal=false, blueAtGoal=false;
14+
15+
P getNext(const P &p, int dir){
16+
return {p.x + dx[dir], p.y + dy[dir]};
17+
}
18+
19+
bool inBounds(const P &p){
20+
return p.x >= 0 && p.x < H && p.y >= 0 && p.y < W;
21+
}
22+
23+
bool isPossibleMove(const P &curR, const P &nR, const P &curB, const P &nB){
24+
// 범위/벽 체크
25+
if (!inBounds(nR) || !inBounds(nB)) return false;
26+
if (mp[nR.x][nR.y] == 5 || mp[nB.x][nB.y] == 5) return false;
27+
28+
// 두 수레가 같은 칸으로 이동하면 안 됨
29+
if (nR.x == nB.x && nR.y == nB.y) return false;
30+
31+
// 서로 위치를 바꿀 수 없음 (swap)
32+
if (curR.x == nB.x && curR.y == nB.y && curB.x == nR.x && curB.y == nR.y) return false;
33+
34+
// 도착해 있지 않은 수레가 이미 방문한 칸으로 돌아가는 것은 불가
35+
if (!redAtGoal && visitedPos[nR.x][nR.y][0]) return false;
36+
if (!blueAtGoal && visitedPos[nB.x][nB.y][1]) return false;
37+
38+
return true;
39+
}
40+
41+
int backtrack(const P &red, const P &blue, int turns){
42+
if (redAtGoal && blueAtGoal) return turns;
43+
int best = INF;
44+
45+
// 각 수레가 움직일 방향(4) × 4 = 16가지
46+
for(int dr = 0; dr < 4; ++dr){
47+
for(int db = 0; db < 4; ++db){
48+
P nR = red, nB = blue;
49+
if (!redAtGoal) nR = getNext(red, dr);
50+
if (!blueAtGoal) nB = getNext(blue, db);
51+
52+
if (!isPossibleMove(red, nR, blue, nB)) continue;
53+
54+
// 상태 변경 적용
55+
bool prevRedGoal = redAtGoal, prevBlueGoal = blueAtGoal;
56+
visitedPos[nR.x][nR.y][0] = true;
57+
visitedPos[nB.x][nB.y][1] = true;
58+
59+
if (mp[nR.x][nR.y] == 3) redAtGoal = true;
60+
if (mp[nB.x][nB.y] == 4) blueAtGoal = true;
61+
62+
best = min(best, backtrack(nR, nB, turns + 1));
63+
64+
// 상태 복구
65+
redAtGoal = prevRedGoal;
66+
blueAtGoal = prevBlueGoal;
67+
visitedPos[nR.x][nR.y][0] = false;
68+
visitedPos[nB.x][nB.y][1] = false;
69+
}
70+
}
71+
return best;
72+
}
73+
74+
// 프로그래머스 형식: solution 함수
75+
int solution(vector<vector<int>> maze) {
76+
H = maze.size();
77+
W = maze[0].size();
78+
P startR{-1,-1}, startB{-1,-1};
79+
80+
for(int i=0;i<H;++i){
81+
for(int j=0;j<W;++j){
82+
mp[i][j] = maze[i][j];
83+
if (mp[i][j] == 1) startR = {i,j}; // 빨간 출발
84+
if (mp[i][j] == 2) startB = {i,j}; // 파란 출발
85+
}
86+
}
87+
88+
// 초기 방문 마크
89+
memset(visitedPos, 0, sizeof(visitedPos));
90+
visitedPos[startR.x][startR.y][0] = true;
91+
visitedPos[startB.x][startB.y][1] = true;
92+
93+
// 만약 시작이 이미 목표에 있다면 해당 수레는 고정(문제 조건에 따라)
94+
redAtGoal = (mp[startR.x][startR.y] == 3);
95+
blueAtGoal = (mp[startB.x][startB.y] == 4);
96+
97+
int ans = backtrack(startR, startB, 0);
98+
return (ans == INF) ? 0 : ans;
99+
}
100+
101+
/*
102+
// 테스트용 main (플랫폼 제출시에는 필요없음)
103+
int main(){
104+
vector<vector<int>> maze = {
105+
{1,0,0,3},
106+
{0,5,0,0},
107+
{2,0,0,4},
108+
{0,0,0,0}
109+
};
110+
cout << solution(maze) << "\n";
111+
return 0;
112+
}
113+
*/
114+

0 commit comments

Comments
 (0)