직접 풀어보고 올리는 코드입니다. 지적이나 더 좋은 방향에 대한 댓글은 항상 환영합니다.
![[백준][C++] 16724번: 피리 부는 사나이 <229> [백준][C++] 16724번: 피리 부는 사나이 <229>](http://t1.daumcdn.net/tistory_admin/static/images/xBoxReplace_250.png)
https://www.acmicpc.net/problem/16724
16724번: 피리 부는 사나이
첫 번째 줄에 지도의 행의 수를 나타내는 N(1 ≤ N ≤ 1,000)과 지도의 열의 수를 나타내는 M(1 ≤ M ≤ 1,000)이 주어진다. 두 번째 줄부터 N개의 줄에 지도의 정보를 나타내는 길이가 M인 문자열이 주...
www.acmicpc.net
생각의 흐름
우선 이 문제를 보고 생각한 방식은 cycle을 찾는 방식이였다.
우리 문제의 예시 를 생각해보자!
3 4
DLLL
DRLU
RRRU
우선 맨 왼쪽 위 부터 탐색을 시작한다고 해보자
맨 처음 모든 칸의 findSafe[][]값은 -1로 초기화 되어있다. 방문한적 없음을 의미한다.
![[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름
[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름](http://t1.daumcdn.net/tistory_admin/static/images/xBoxReplace_250.png)
(0,0)지점은 지금 방문했기 때문에 res값인 1을 담아둔다.
이후 다음 이동 지점의 알파벳을 따라서 방향을 진행하다 보면 다음과 같은 순간이 온다.
![[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름
[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름](http://t1.daumcdn.net/tistory_admin/static/images/xBoxReplace_250.png)
(0,1) 에서 이전에 방문했었던 (0,0) 에 다시 접근하려 할때 해당 (0,0)칸의 findSafe[][]의 값은 1이다.
따라서 이전에 방문했었던적이 있다는 의미이다.
이때 그냥 다음과 같이 (0,0)의 findSafe의 값을 바로 반환시켜버리면 된다.
if(findSafe[x][y] != -1) {
return findSafe[x][y];
}
이렇게 하면 BFS를 호출했던 역순으로 되돌아 올라가면서 해당 칸의 findSafe값을 직전 함수의 호출로 전달하는 것 이다.
따라서 탐색 이후의 findSafe의 값들은 다음과 같다.
![[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름
[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름](https://blog.kakaocdn.net/dn/cXUe7W/btrFoCT7i90/2TORr2OfM7QxVBxSk9ExOK/img.png)
해당 칸에 findSafe 값들이 적혀있다.
하지만 아래 코드를 보면 의문이 들수도 있다. DFS의 큰 틀은 다음과 같은데...
int DFS(int x, int y) {
if(findSafe[x][y] != -1) {
return findSafe[x][y];
}
findSafe[x][y] = res; // 직전에 값을 대입
// 생략...
int nx = x + dx[dir];
int ny = y + dy[dir];
findSafe[x][y] = DFS(nx, ny); // 호출 이후에도 대입
return findSafe[x][y];
}
호출 직전에는 왜 값을 대입할까? DFS의 호출후 반환값만 findSafe에 대입해주면 되는 것 아닐까?
cycle을 찾으면 해당 시점에 백트래킹을 하면서 findSafe값을 채워주면 될거라는 생각이였다.
하지만 호출 전에도 값을 채우는 이유가 있었다.
만약 다음과 같이 그레프가 있었다고 해보자.
![[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름
[백준][C++] 16724번: 피리 부는 사나이 <229> -
생각의 흐름](https://blog.kakaocdn.net/dn/cBSkLL/btrFoCfxHDb/pGLqu60IcKuElf7SgyB8E0/img.png)
원래 L이였던 칸이 R로 바꾼 상태이다.
2번째 탐색이기 때문에 res++ 을 진행해준다.
이러면 2번째 탐색을 (1,1)을 에서 시작할때 (1,1) -> (1,2) -> (1,3)으로 이동하는데
직전 대입때는 findSafe에 2를 대입(res++를 했으니)하게 되고, (1,3) 칸에 도착했을때는 BFS 반환값으로 1을 반환하게 되니 findSafe에 1을 저장하게 된다.
safezone이 1개만 있으면 되는 상황이기 때문에 1을 저장하는것이 정확하다.
하지만 원래대로 R이 아니라, L 이였다면 (1,1) -> (1,2) -> (1,1) 로 되어 2로 값이 채워지게 된다.
나의 코드
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
#define MAX 1001
int N, M;
int dx[4] = { -1,0,1,0};
int dy[4] = { 0,1,0,-1};
char MAP[MAX][MAX];
int findSafe[MAX][MAX];
int res = 1;
int DFS(int x, int y) {
if(findSafe[x][y] != -1) {
return findSafe[x][y];
}
findSafe[x][y] = res;
int dir;
if (MAP[x][y] == 'U') {
dir = 0;
} else if (MAP[x][y] == 'R') {
dir = 1;
} else if (MAP[x][y] == 'D') {
dir = 2;
}else if(MAP[x][y] == 'L') {
dir = 3;
}
int nx = x + dx[dir];
int ny = y + dy[dir];
int ret = DFS(nx, ny);
findSafe[x][y] = ret;
return findSafe[x][y];
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cin >> N >> M;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> MAP[i][j];
findSafe[i][j] = -1;
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (findSafe[i][j] == -1) {
DFS(i, j);
res++;
}
}
}
set<int> s;
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)
s.insert(findSafe[i][j]);
cout << s.size();
return 0;
}
'Algorithm > 백준' 카테고리의 다른 글
[백준][C++] 11085번: 군사 이동 <231> (0) | 2022.07.01 |
---|---|
[백준][C++] 20040번: 사이클 게임 <230> (0) | 2022.06.23 |
[백준][C++] 9328번: 열쇠 <228> (0) | 2022.06.10 |
[백준][C++] 1389번: 케빈 베이컨의 6단계 법칙 <227> (0) | 2022.06.08 |
[백준][C++] 2098번: 외판원 순회 <226> (0) | 2022.06.03 |
댓글