컴퓨터 사이언스/1고리즘

백준 19237 : 어른 상어

저세상 개발자 2021. 8. 9. 01:15

https://www.acmicpc.net/problem/19237

 

19237번: 어른 상어

첫 줄에는 N, M, k가 주어진다. (2 ≤ N ≤ 20, 2 ≤ M ≤ N2, 1 ≤ k ≤ 1,000) 그 다음 줄부터 N개의 줄에 걸쳐 격자의 모습이 주어진다. 0은 빈칸이고, 0이 아닌 수 x는 x번 상어가 들어있는 칸을 의미

www.acmicpc.net

 

풀기 싫게 생겨서 미루고 미루다 이제야 푼 문제.

단순 깡 구현인데 깔끔하게 짜고싶었지만 별로 그렇지 않은 것 같다.

 

문제 해결 논리는 다음과 같다.

 

0. 상어는 어차피 한 턴에 한 방향으로만 움직이므로 상어 정보를 구조체 배열로 관리하고 상어 번호만 큐에 넣어 이동할 상어와 격자 밖으로 쫓겨난 상어를 구분한다.

1. 판 위에 이미 뿌려진 체취가 옅어지고 상어가 현재 위치에 체취를 뿌리고 다음 칸으로 이동하는 것을 한 턴으로 본다.

2. 턴이 시작되는 시점에 모든 상어가 체취를 다 뿌리고 나서 상어를 하나씩 이동시킨다.

   상어를 한 마리 씩 체취 뿌리고 이동하고 하는 식으로 할 시 상어의 순서에 따라 상어의 이동 방향이 달라질 수 있다

 

코드로 구현하면 아래와 같다.

#include <iostream>
#include <queue>

#define SHARK first
#define SMELL second

struct SharkInfo {
	int x, y, dir;
};

using namespace std;
using pii = pair<int, int>;

int n, m, k;
pii board[21][21];

int dir[401][5][4];
int dxdy[5][2] = { {0, 0}, {0, -1}, {0, 1}, {-1, 0}, {1, 0} };

queue<int> q;
SharkInfo sharks[401];

void input() {
	int tmp;

	//상어 정보 갱신 및 큐에 상어 번호 추가
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> tmp;
			if (tmp)
				sharks[tmp] = { j, i, 0 };
		}
	}

	for (int i = 1; i <= m; i++) {
		cin >> sharks[i].dir;
		q.emplace(i);
	}

	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= 4; j++) {
			for (int k = 0; k < 4; k++) {
				cin >> dir[i][j][k];
			}
		}
	}
}

int bfs() {
	SharkInfo* cur;
	int nx, ny, ndir, cur_num;
	int remain = 0, turn = -1;

	while (!q.empty() && turn <= 1000) {
		//턴 시작 시
		if (remain <= 0) {
			++turn; remain = q.size();
			//체취 감소 및 제거
			for (int y = 0; y < n; y++) {
				for (int x = 0; x < n; x++) {
					if (board[y][x].SMELL) {
						--board[y][x].SMELL;
						if (!board[y][x].SMELL)
							board[y][x].SHARK = 0;
					}
				}
			}

			//모든 상어가 체취를 뿌림
			for (int i = 0; i < remain; i++) {
				cur_num = q.front();
				cur = &sharks[cur_num];
				q.pop();

				//다른 상어에게 잡아먹힌 경우
				//체취가 없거나 본인 체취인 곳에만 이동이 가능하므로
				//현재 위치에 다른 상어의 체취가 있다는 것은 본인보다 더 빠른 번호의
				//상어가 이번 턴에 체취를 뿌렸다는 것, 따라서 해당 상어는 큐에 넣지 않는다.
				if (board[cur->y][cur->x].SHARK &&
					board[cur->y][cur->x].SHARK != cur_num) {
					continue;
				}

				board[cur->y][cur->x].SHARK = cur_num;
				board[cur->y][cur->x].SMELL = k;

				q.emplace(cur_num);
			}

			//만약 상어가 한 마리만 남았다면 종결
			if (q.size() <= 1) break;
			//큐에 들어있는 원소들을 한번씩 싹 다 꺼내서 처리하면 한 턴이 지난걸로 한다.
			//턴을 체크하기 위해 큐에 들어있는 원소의 개수를 저장한다.
			remain = q.size();
		}
		
		//상어 이동
		cur_num = q.front();
		cur = &sharks[cur_num];
		q.pop();

		for (int i = 0; i < 5; i++) {
			//0~3 까지는 주변에 빈 곳 탐색
			if (i < 4) {
				ndir = dir[cur_num][cur->dir][i];
				nx = cur->x + dxdy[ndir][0];
				ny = cur->y + dxdy[ndir][1];

				if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
				if (board[ny][nx].SHARK) continue;

				break;
			}
			//4까지 올 시 주변에 빈 곳이 없는 것이므로
			//자신의 체취가 있는 곳 중 우선순위가 높은 곳으로 이동
			else {
				for (int i = 0; i < 4; i++) {
					ndir = dir[cur_num][cur->dir][i];
					nx = cur->x + dxdy[ndir][0];
					ny = cur->y + dxdy[ndir][1];

					if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
					if (board[ny][nx].SHARK != cur_num) continue;

					break;
				}
			}
		}

		//상어 정보 배열 갱신
		cur->x = nx, cur->y = ny, cur->dir = ndir;
		q.emplace(cur_num);

		--remain;
	}

	return turn > 1000 ? -1 : turn;
}

void solution() {
	input();
	cout << bfs();
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	solution();

	return 0;
}
메모리: 2064 kb 시간: 0 ms