https://www.acmicpc.net/problem/15684
15684번: 사다리 조작
사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선
www.acmicpc.net
이 문제는 DFS와 백트래킹을 사용하는 문제입니다.
문제 정리
사다리 타기를 하는데 1번 위치가 사다리를 타면 1번 위치로 내려가고 2번은 2번 위치, 즉, i번이 사다리를 탔을 때 결과가 i번이도록 사다리에 가로선을 추가하는 것입니다.
가로선은 최대 3까지 가능하며 가로선을 최소로 하는 사다리를 만드는 것입니다.
문제 해결 방법
사다리 표시 방법 부터 정해야 합니다. 배열로 정의를 할 것이고 저는 ladder_pos[y][x]로 정의 했습니다.
y는 가로선의 번호, x는 세로선의 번호를 뜻합니다. (저는 y는 0부터 시작하고 x는 1부터 시작하도록 코드를 짰습니다.)
ladder_pos[0][1] = true 라는 것은
이런식으로 높이 0번째에 1번에서 2번을 이어주는 가로선이 있다는 뜻입니다.
정리하면 ladder_pos[y][x] => 높이 y번째에 x번 에서 x+1번째를 이어주는 가로선이다.
먼저 우리는 가로선을 최소로 할 것이기 때문에 나누어서 생각해야합니다.
가로선을 추가 하지 않을 때, 1개 추가할 때, 2개 추가할 때 3개 추가할 때로 나누어 생각합니다.
이제 dfs를 사용할 것인데 먼저 가로선의 추가 개수 여부를 체크하여서 가로선을 추가해야할 경우와 그렇지 않은 경우로 나누어 줍니다.
만약 가로선을 더 이상 추가하지 않아도 된다면 사다리가 정상인지 확인해주어야 합니다.
확인하는 방법은 세로선 시작 위치를 verti 변수에 저장해 줍니다. 그 후 내려갈 건데 내려갈 때 마다 위치를 확인하여 가로선이 그어져 있는지 확인합니다. 위 그림 경우에서 ladder_pos[0][1] = true 이므로 verti 에 +1를 해주면 됩니다.
즉, ladder_pos[현재 높이][현재 verti]가 true이면 오른쪽으로 이동합니다.
만약 verti > 1 이고 ladder_pos[현재 높이][현재 verti - 1]이 true라면 왼쪽으로 이동합니다. 이것은 위 그림에서 만약 처음 위치가 2번이라고 생각하면 됩니다. 그럼 verti가 2가 되고 현재 높이는 0 이기 때문에 ladder_pos[0][2-1] = true 이므로 사다리를 타면 왼쪽으로 가게 됩니다.
이렇게 해서 하나의 세로선에 대해 모든 높이에 대해서 했을 때 verti의 결과가 처음 위치와 같으면 이 세로선은 가능하므로 다음 세로선을 확인하면 됩니다. 만약 다르다면 이 사다리는 잘못된 사다리 이므로 bool 변수에 표시를 하고 다음 세로선을 확인하지 않습니다.
그 다음 다른 가로선을 추가해 다른 사다리의 경우를 확인해 줍니다.
만약 모든 세로선이 정상이라면 답이기 때문에 flag에 true를 해줍니다. 그 후 ladder_cnt를 출력해주면됩니다.
가로선을 추가하는 방법
모든 가로선을 추가할 수 있는 경우의 수를 따져줄 것인데 이때 만약 가로선을 2개 추가하는 예를 들면 먼저 하나를 추가한 것을 고정해 놓고 다음 부분을 추가 하고 다음 추가하고 확인하고 이런식으로 갑니다. 또한 사다리를 추가할 때 문제 조건에서 곂치거나 점을 공유 하면 안됩니다. 그러므로 추가 하려는 점 양 쪽으로 사다리가 있는지 확인후에 없으면 추가합니다.
이렇게 사다리를 추가한 후 백트래킹을 이용해서 dfs에 넣는데 이 때 가로선 위치 추가한 y좌표와 가로선 추가를 했기 때문에 가로선 추가 카운트를 해줍니다. 가로선 위치 추가한 y좌표를 넣는 이유는 앞에서 이미 사다리를 추가하면서 확인했기 때문에 중복으로 확인할 필요가 없기 때문입니다. 그 후 만약 이 사다리가 잘못된 경우 다음 위치에 사다리를 추가할 수 있도록 추가한 사다리를 없애줍니다.
말로 이해가 잘 안된다면 밑에 코드에 주석을 달아놨습니다.
천천히 보시면 이해가 될 것입니다.
전체코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#include <iostream>
using namespace std;
int n, m, h;
bool ladder_pos[31][11];
int ladder_cnt; // 가로선을 추가할 개수
bool flag = false;
void dfs(int y, int cnt){
if(ladder_cnt == cnt){
bool possible = true; // 각각의 세로선이 잘 도착했는지 여부체크
for (int i = 1; i <= n; i++){ // 세로선
int verti = i;
for (int j = 0; j < h; j++){ // 가로선
int hight = j;
if(ladder_pos[hight][verti]){ // 오른쪽으로 가는 경우
verti++;
}else if(verti > 1 && ladder_pos[hight][verti - 1]){
//왼쪽으로 가는 경우
verti--;
}
}
if(verti != i){ // 잘못된 세로선
possible = false;
break;
}
}
if(possible){
flag = true; // 정답
}
return;
}
// 가로선 추가하는 코드
for (int i = y; i < h;i++){
for (int j = 1; j < n; j++){
if(!ladder_pos[i][j-1] && !ladder_pos[i][j] && !ladder_pos[i][j+1]){
//사다리가 추가 될 수 있는 위치인지 확인
ladder_pos[i][j] = true; // 사다리 추가
dfs(i, cnt + 1); // y좌표와 사다리 추가 개수 카운트
ladder_pos[i][j] = false; // 추가한 사다리 삭제
}
}
}
return;
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m >> h;
for (int i = 0; i < m; i++){
int a, b;
cin >> a >> b;
ladder_pos[a-1][b] = true; //문제에서 준 가로선 표시
}
for (int i = 0; i <= 3; i++){ // 가로선 추가 개수마다 확인
ladder_cnt = i;
dfs(0, 0);
if(flag){ // 정답을 찾은 경우
cout << ladder_cnt << endl;
return 0;
}
}
cout << -1 << endl; // 정답을 찾지 못한 경우
return 0;
}
|
cs |
체감 난이도 | 걸린시간 | 후기 | 사용 알고리즘 |
중상 | 1.5h | 처음에 문제를 어떻게 풀어야할 지 감을 못잡았음 어떤식으로 접근하는지 참고 후에 풀림 |
DFS 백트래킹 |
'알고리즘 > 백준' 카테고리의 다른 글
백준 2343 기타레슨 c++ [컴공과고씨] (0) | 2022.08.10 |
---|---|
백준 6236 용돈 관리 c++ [컴공과고씨] (0) | 2022.08.09 |
백준 12851 숨박꼭질2 C++ [컴공과고씨] (0) | 2022.08.08 |
백준 14889 스타트와 링크 c++ [컴공과고씨] (0) | 2022.05.05 |
백준 16928 뱀과 사다리 게임 c++ [컴공과고씨] (0) | 2022.05.03 |