Problem Solving/백준

[BOJ, 삼성 SW 역량 테스트 기출 문제] 16236번 : 아기상어

돌돌김 2020. 10. 9. 17:47

www.acmicpc.net/problem/16236

 

16236번: 아기 상어

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가��

www.acmicpc.net

 

문제 조건을 잘 읽고 그대로 구현하면 되는 문제이다.

 

구현 자체는 쉬운데, 문제를 제대로 안읽어서 놓친 조건과 문제에서 정보를 안준 것이 하나 있어서 좀 오래 걸렸다.

 

 

문제 조건을 요약하면 아래와 같다.

 

아기 상어의 이동
  • 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없음
  • 나머지 칸은 모두 지나갈 수 있다.

 

물고기 먹기
  • 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다.
  • 크기가 같은 물고기는 먹을 수 없다.

 

어떤 물고기를 먼저 먹는지?
  • 더 이상 먹을 수 있는 물고기가 공간에 없다면 아기 상어는 엄마 상어에게 도움을 요청한다.(종료조건)
  • 먹을 수 있는 물고기가 1마리라면, 그 물고기를 먹으러 간다.
  • 먹을 수 있는 물고기가 1마리보다 많다면, 거리가 가장 가까운 물고기를 먹으러 간다.
    • 거리는 아기 상어가 있는 칸에서 물고기가 있는 칸으로 이동할 때, 지나야하는 칸의 개수의 최솟값이다.
    • 거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다.
  • 아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다.
  • 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.

 

 

아기 상어 크기 증가
  • 아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다.
    • 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.
    • 크기가 3이 된 아기상어가 크기가 4가 되려면 물고기를 추가로 3마리 더 먹어야 한다.
    • 현재까지 먹은 물고기가 2마리이고, 1마리를 더 먹는다고 크기가 증가하는게 아니다.

위에서 하이라이트 표시한 부분은 문제에 적혀있지 않았다. 물고기를 먹고나면 소화가 되는건지..

 

 

문제 해결
  • 입력을 받으면서 아기 상어의 위치 정보를 저장하고 '9' 였던 것을 '0'으로 초기화 해준다.
  • fish에는 현재 아기 상어가 먹을 수 있는 물고기들을 넣었다.
  • 전형적인 bfs 탐색을 한다.
  • fish 벡터가 비어있다면, 먹을 수 있는 물고기가 없으므로 return 한다.
  • 현재 아기상어의 위치에서 가장 가까운 물고기를 찾고, 거리가 같은 물고기들이 여럿있다면 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 찾아야 하므로 사용자 정의 정렬 함수를 따로 만들어서 바로 다음에 먹으러 갈 물고기를 맨 앞에 두었다.
  • 맨 앞에 있는 물고기가 아기상어가 지금 먹을 물고기 이므로, 해당 위치로 아기상어를 옮기고 해당 위치의 값을 0으로 해준다
    • 이 때, 자기 자신의 크기와 같은 수의 물고기를 먹으면 크기가 1 증가하므로 확인해준다.
    • 크기가 증가했다면, 지금까지 먹은 물고기의 수를 0으로 만들어준다.
    • 물고기를 먹었으므로, 해당 위치까지 이동하는데 걸린 거리를 더해준다.

 

소스코드
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;

struct SHARK {
	int y;
	int x;
	int size;
};
struct FISH {
	int y;
	int x;
	int size; 
	int dist; // 상어와 물고기 사이의 거리
};

struct DIST {
	int y;
	int x;
	int dist;
};

int map[21][21];
bool visited[21][21];
int n, answer;
SHARK shark; // 아기 상어
vector<FISH>fish; // 물고기
int fish_count = 0; // 먹은 물고기 수


int dy[4] = { 0,0,-1,1 };
int dx[4] = { 1,-1,0,0 };

bool cmp(FISH a, FISH b) { // 거리가 가장 가까운 물고기, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기 순으로 정렬
	if (a.dist == b.dist) {
		if (a.y == b.y) {
			if (a.x == b.x) {
				return a.size < b.size;
			}			
			return a.x < b.x;
		}
		return a.y < b.y;
	}
	return a.dist < b.dist;
}

// 상어에서 물고기 까지의 거리 계산
void bfs() {
	queue<DIST>q;
	fish.clear();
	memset(visited, false, sizeof(visited));
	q.push({ shark.y, shark.x, 0 });
	visited[shark.y][shark.x] = true;
	while (!q.empty())
	{
		int y = q.front().y;
		int x = q.front().x;
		int dist = q.front().dist;
		q.pop();

		if (map[y][x]<shark.size && map[y][x]!=0) { // 현재 먹을 수 있는 물고기를 만남				
			fish.push_back({ y,x,map[y][x],dist});
		}
		for (int dir = 0; dir < 4; dir++) {
			int ny = y + dy[dir];
			int nx = x + dx[dir];
			int ndist = dist + 1;
			if (0 > ny || ny > n - 1 || 0 > nx || nx > n - 1)continue;
			if (visited[ny][nx])continue; // 방문
			if (map[ny][nx] > shark.size)continue; // 크기가 더 큰 물고기는 못지나감
			q.push({ ny,nx,ndist });
			visited[ny][nx] = true;
		}
	}
	if (fish.empty())
		return;
	sort(fish.begin(), fish.end(), cmp);
	// 물고기 잡아 먹기
	shark.y = fish.front().y;
	shark.x = fish.front().x;
	fish_count++;
	if (shark.size == fish_count) { // 아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가
		shark.size += 1;
		fish_count = 0;
	}
	answer += fish.front().dist;
	map[fish.front().y][fish.front().x] = 0;	
	return;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	freopen("input.txt", "r",stdin);
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> map[i][j];
			if (map[i][j] == 9) {
				shark.y = i;
				shark.x = j;
				shark.size = 2;
				map[i][j] = 0;
			}
		}
	}
	
	do
	{
		bfs();
	} while (!fish.empty());
	cout << answer;
}