문제 : https://www.acmicpc.net/problem/13460
( 저번 포스팅 : https://travelbeeee.tistory.com/322 )
( 참고한 포스팅 : https://jason9319.tistory.com/217 )
[ 알고리즘 ]
내가 구현한 알고리즘이 다른 알고리즘에 비해 수행 시간 면에서 너무 느리고 단순히 생각해봐도 꼭 백트레킹을 이용해 10번 회전하는 모든 경우의 수에 대해서 찾은 다음에 회전할 필요도 없이, 회전을 진행해나가며 답을 찾으면 Stop 하는 것이 효율적임을 알 수 있었다. 따라서, 문제를 다시 풀어보았다.
하지만, BFS탐색을 이용해서 경우의 수를 늘려가더라도, 현재 'R', 'B' 위치에 따라서 Map의 모양이 바뀌는데 이 문제를 어떻게 해결해야 하나 답을 찾지 못했다,,,,
그래서 다른 고수분들의 코드를 공부하던 중, '자손9319' 님의 코드가 너무 아름다워서 분석을 해보려고 한다,,,!
아래는 '자손9319' 님의 코드다.
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
int n, m, disc[11][11][11][11], bx, by, rx, ry;
char a[11][11];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
int bfs() {
queue<pair<pair<int, int>, pair<int,int>>> qu;
qu.push({ {rx,ry},{bx,by} });
disc[rx][ry][bx][by] = 1;
int ret = 0;
while (qu.size()) {
int qs = qu.size();
while (qs--) {
int x = qu.front().first.first;
int y = qu.front().first.second;
int z = qu.front().second.first;
int w = qu.front().second.second;
qu.pop();
if (a[x][y] == 'O'&&a[x][y] != a[z][w])
return ret;
for (int i = 0; i < 4; i++) {
int cx = x, cy = y, cz = z, cw = w;
while (a[cx + dx[i]][cy + dy[i]] != '#'&&a[cx][cy] != 'O') {
cx += dx[i];
cy += dy[i];
}
while (a[cz + dx[i]][cw + dy[i]] != '#'&&a[cz][cw] != 'O') {
cz += dx[i];
cw += dy[i];
}
if (cx == cz&&cy == cw) {
if (a[cx][cy] == 'O')continue;
if (abs(cx - x) + abs(cy - y) < abs(cz - z) + abs(cw - w)) {
cz -= dx[i];
cw -= dy[i];
}
else {
cx -= dx[i];
cy -= dy[i];
}
}
if (disc[cx][cy][cz][cw])continue;
qu.push({ {cx,cy} ,{cz,cw} });
disc[cx][cy][cz][cw] = 1;
}
}
if (ret == 10)
return -1;
ret++;
}
return -1;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
getchar();
for (int j = 0; j < m; j++) {
scanf("%c", &a[i][j]);
if (a[i][j] == 'B') {
bx = i;
by = j;
}
else if (a[i][j] == 'R') {
rx = i;
ry = j;
}
}
}
printf("%d\n", bfs());
return 0;
}
출처: https://jason9319.tistory.com/217 [ACM-ICPC 상 탈 사람]
1. disc 4차원 배열.
disc 4차원 배열을 통해 BFS 탐색을 진행하면서 이미 과거에 'R', 'B' 가 현재 위치에 있었던 경우는 탐색을 중단한다.
2. dx, dy 배열
dx, dy 배열을 통해서 4개의 방향에 대해서 깔끔하게 이동할 수 있다. 이때, '#', 'O'가 아닌 경우에는 계속 이동을 진행한다.
3. 빨간 구슬과 파란 구슬이 겹치는 경우 해결.
예를 들어 오른쪽으로 기울여야 되는데 빨간 구슬과 파란 구슬이 같은 행에 있고, 빨간 구슬이 파란 구슬보다 더 왼쪽에 있다고 하자. 그렇다면, 더 오른쪽에 있는 파란 구슬을 먼저 이동해야 하므로 파란 구슬을 이동시키고, 빨간 구슬을 이동시켜야 한다고 생각했고, 그러다 보니 기존의 내 코드는 복잡해졌다.
하지만, '자손9319' 님은 일단 이동시킨 후에, 둘이 겹친다면( 즉 기울이는 과정에서 하나가 먼저 이동하고 다른 하나가 이동해야 하는 경우 ) 아래의 코드를 통해서 깔끔하게 문제를 해결하는 것을 볼 수 있다.
if (abs(cx - x) + abs(cy - y) < abs(cz - z) + abs(cw - w)) {
cz -= dx[i];
cw -= dy[i];
}
else {
cx -= dx[i];
cy -= dy[i];
}
내가 걱정하던 현재 진행되는 경우에서 'R', 'B' 의 위치에 따라 map이 조금씩 다른데 이 부분을 어떻게 해결할까는 사실 이 문제에서 고려를 하지 않아도 되는 부분이었다. 그냥 'R', 'B' 위치가 있으면 모든 방향에 대해서 기울이고 우리는 '#', 'O' 인지 아닌지만 신경 쓰면 됐다....! 내가 지금 이동시킨 곳이 'R' 인지 'B' 인지는 신경 쓰지 않아도 이동시키고 빨간 구슬과 파란 구슬이 겹친다면 위의 코드를 이용해 위치를 적절히 갱신해주면 되는 일이었다.
알고리즘 잘하고 싶다,,,