N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.
아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.
아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.
아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.
아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.
아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.
공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.
첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.
둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.
아기 상어는 공간에 한 마리 있다.
첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.
https://www.acmicpc.net/problem/16236
문제를 제대로 이해하는게 중요하다
먹을수있는 먹이가 있을때 가장위, 가장왼쪽을 우선순위로 한다는 말이
상어의 현재 좌표 기준이 아니라 맵전체에서 가장위 가장왼쪽을 의미하는것이다.
따라서 BFS로 먹을수있는 먹이를 찾는순간
해당 먹이까지의 걸린 시간이 최소시간이니
동일한 시간으로 닿을 수 있는 모든 먹잇감중에
x좌표가 가장 작고 y좌표가 가장 작은 것을 먹어야함이다.
처음에 단순하게 상, 좌, 우, 하로 탐색하면 되는 문제인줄 알아서 애를 좀 먹었다.
이때 찾은 물고기들의 정보를 구조체로 담았는데
구조체내의 정렬은 아래와 같은 코드로 작성할 수 있다.
loc라는 구조체가있을때 comp함수를 이렇게 작서하면
49번째 줄처럼 true가되는것이 먼저 정렬된다.
따라서 문제에서처럼 x 그리고 y가 작은순으로 앞으로 오도록 정렬할 수 있다.
사용은 아래처럼 한다.
아래는 전체 소스코드이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <queue>
using std::vector; using std::queue; using std::pair;
int dxy[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
/*
X Y : 상어 좌표(실시간)
S : 상어크기
E : 상어가 먹은 물고기갯수
cnt : 크기가1~6 인 물고기의 갯수를 기록
크기별 물고기갯수를 카운트해놓음 >> cnt
1. 현재상어크기보다 작은 물고기가 없으면 프로그램종료
2. 현재맵에서find
> 만약 불가능하면 BFS하며 쓰인 시간은 더하지않고 프로그램 종료
3. BFS진행에 들인 시간을 더함
*/
int N, map[20][20], X, Y, S = 2, E, cnt[7];
void init() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0, in; j < N; j++) {
scanf("%d", &in);
if (in == 9) { //상어위치는 맵에 기록하지않음
X = i; Y = j;
continue;
}
map[i][j] = in; //물고기 위치를 맵에 기록
if(1 <= in || in <= 6) {
cnt[in]++; //물고기 크기별 갯수를 셈
}
}
}
}
/*
BFS진행하며 물고기를 먹으러가서
불가능하다면 -1을 리턴
가능하다면 BFS에 들인 시간을 리턴
*/
struct loc{ //물고기의 위치, 탐색에 쓰인 시간을 기록하는 컨테이너
int x, y, t;
};
bool comp(const loc& l1, const loc& l2) { //loc는 x가작고 y가 작은것순으로 정렬된다.
if (l1.x < l2.x)
return true;
else if (l1.x == l2.x)
return l1.y < l2.y;
else
return false;
}
int find() {
queue<loc> q;
q.push({ X, Y, 0});
bool visited[20][20] = { false, };
visited[X][Y] = true;
int max = 0x7fffffff; //BFS하며 찾을 물고기의 최대 거리
vector<loc> list;
while (!q.empty()) {
int cx = q.front().x;
int cy = q.front().y;
int ct = q.front().t;
q.pop();
if (max < ct) continue;
if (0 < map[cx][cy] && map[cx][cy] < S) { //크기가 작은 물고기를 찾은 경우
max = ct; //찾을 물고기의 최대거리를 변경
list.push_back({ cx, cy, ct }); //기록
}
for (int dir = 0; dir < 4; dir++) {
int nx = cx + dxy[dir][0];
int ny = cy + dxy[dir][1];
int nt = ct + 1;
if (nx < 0 || ny < 0 || nx == N || ny == N || visited[nx][ny]) continue;
if (map[nx][ny] > S) continue; //상어크기보다 큰 물고기는 지나갈 수 없다.
visited[nx][ny] = true;
q.push({ nx, ny, nt});
}
}
if (list.size() == 0) return -1; //불가능 -1리턴
std::sort(list.begin(), list.end(), comp); //x좌표, y좌표순으로 작은것이 먼저 오도록 정렬
int x = list[0].x;
int y = list[0].y;
int t = list[0].t;
map[x][y] = 0; //물고기를 먹는다.
cnt[map[x][y]]--; //세놓은 물고기 갯수 -1
X = x; //상어 위치 이동
Y = y;
E++;
if (E == S) { //상어가 크기만큼 물고기를 먹으면 크기+1
S++;
E = 0;
}
return t; //걸린 시간을 리턴
}
//프로그램 메인
void logic() {
int res = 0;
while (1) {
int end = false;
for(int i = 1 ; i < S ; i++)
if (cnt[i]) {
end = true;
break;
}
//상어가 먹을수있는 물고기가 없으면 프로그램종료
if (!end) break;
int time = find(); //상어가 물고기를 찾는다.
if (time == -1) break; //물고기를 못찾았으면 프로그램종료
res += time; //걸린 시간을 기록
}
printf("%d", res);
}
int main(void) {
init();
logic();
return 0;
}