https://www.acmicpc.net/problem/16236
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<vector<int>> arr;
bool visit[21][21];
int dy[4] = {-1, 0, 0, 1};
int dx[4] = {0, -1, 1, 0};
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n;
int ans = 0;
cin >> n;
arr.resize(n, vector<int>(n));
pair<int, int> eat;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> arr[i][j];
if (arr[i][j] == 9) {
arr[i][j] = 0;
eat = make_pair(i, j);
}
}
}
int s = 2;
int eCnt = 0;
while (true) {
queue<pair<int, pair<int, int>>> q;
fill_n(*visit, 21 * 21, false);
q.push(make_pair(0, make_pair(eat.first, eat.second)));
visit[eat.first][eat.second] = true;
bool isEat = false;
int eatTime;
while (!q.empty()) {
int cnt = q.front().first;
pair<int, int> cur = make_pair(q.front().second.first, q.front().second.second);
if (arr[cur.first][cur.second] > 0 && arr[cur.first][cur.second] < s && eatTime == cnt) {
if (eat.first > cur.first || (eat.first == cur.first && eat.second > cur.second)) {
eat = cur;
continue;
}
}
q.pop();
for (int i = 0; i < 4; i++) {
pair<int, int> ncur = make_pair(cur.first + dy[i], cur.second + dx[i]);
if (ncur.first >= 0 && ncur.first < n && ncur.second >= 0 && ncur.second < n && !visit[ncur.first][ncur.second]){
if (arr[ncur.first][ncur.second] <= s) {
if (arr[ncur.first][ncur.second] > 0 && s > arr[ncur.first][ncur.second] && !isEat) {
isEat = true;
eat = ncur;
eatTime = cnt + 1;
ans += eatTime;
} else {
q.push(make_pair(cnt + 1, make_pair(ncur.first, ncur.second)));
visit[ncur.first][ncur.second] = true;
}
}
}
}
}
if (isEat) {
isEat = false;
eCnt++;
arr[eat.first][eat.second] = 0;
if (eCnt == s) {
s++;
eCnt = 0;
}
} else {
break;
}
}
cout << ans;
return 0;
}
1. 문제
아래와 같은 의미를 가진 n*n의 행렬이 주어진다.
- 0: 빈 칸
- 1, 2, 3, 4, 5, 6: 칸에 있는 물고기의 크기
- 9: 아기 상어의 위치
아기 상어는 최대한 많은 양의 물고기를 먹으려는 데 다음의 제약조건이 따른다.
- 더 이상 먹을 수 있는 물고기가 공간에 없다면 아기 상어는 엄마 상어에게 도움을 요청한다.
- 먹을 수 있는 물고기가 1마리라면, 그 물고기를 먹으러 간다.
- 먹을 수 있는 물고기가 1마리보다 많다면, 거리가 가장 가까운 물고기를 먹으러 간다.
- 거리는 아기 상어가 있는 칸에서 물고기가 있는 칸으로 이동할 때, 지나야하는 칸의 개수의 최솟값이다.
- 거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다.
- 아기 상어 크기 이하의 물고기가 있는 공간은 지나갈 수 있다.
- 아기 상어 크기 미만의 물고기는 먹을 수 있다.
- 아기 상어의 이동에 1초가 걸린다.
- 아기 상어의 크기와 같은 개수의 물고기를 먹으면 아기 상어의 크기가 1 증가한다.
이 때 몇 초 동안 아기 상어가 물고기를 잡아먹을 수 있는 지 구하라.
2. 풀이
상당히 복잡했던 구현 문제이다. 하나씩 살펴보자.
pair<int, int> eat;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> arr[i][j];
if (arr[i][j] == 9) {
arr[i][j] = 0;
eat = make_pair(i, j);
}
}
}
eat는 각 bfs의 시작 지점이다. 처음 아기 상어의 위치이자 각 먹이 활동이 끝나고의 위치이기도 하다. 입력을 받으면서 9를 찾으면 해당 위치는 아기 상어의 처음 위치이므로 초기화해준다.
int s = 2;
int eCnt = 0;
s는 아기 상어의 크기 eCnt는 상어가 먹은 물고기의 개수를 뜻한다.
queue<pair<int, pair<int, int>>> q;
fill_n(*visit, 21 * 21, false);
q.push(make_pair(0, make_pair(eat.first, eat.second)));
visit[eat.first][eat.second] = true;
bool isEat = false;
int eatTime;
while (!q.empty()) {
int cnt = q.front().first;
pair<int, int> cur = make_pair(q.front().second.first, q.front().second.second);
if (arr[cur.first][cur.second] > 0 && arr[cur.first][cur.second] < s && eatTime == cnt) {
if (eat.first > cur.first || (eat.first == cur.first && eat.second > cur.second)) {
eat = cur;
continue;
}
}
q.pop();
for (int i = 0; i < 4; i++) {
pair<int, int> ncur = make_pair(cur.first + dy[i], cur.second + dx[i]);
if (ncur.first >= 0 && ncur.first < n && ncur.second >= 0 && ncur.second < n && !visit[ncur.first][ncur.second]){
if (arr[ncur.first][ncur.second] <= s) {
if (arr[ncur.first][ncur.second] > 0 && s > arr[ncur.first][ncur.second] && !isEat) {
isEat = true;
eat = ncur;
eatTime = cnt + 1;
ans += eatTime;
} else {
q.push(make_pair(cnt + 1, make_pair(ncur.first, ncur.second)));
visit[ncur.first][ncur.second] = true;
}
}
}
}
이후 bfs를 돌린다. 해당 bfs로 물고기를 하나씩 먹을 것이다. 하나씩 살펴보자.
queue<pair<int, pair<int, int>>> q;
fill_n(*visit, 21 * 21, false);
q.push(make_pair(0, make_pair(eat.first, eat.second)));
visit[eat.first][eat.second] = true;
bool isEat = false;
int eatTime;
큐를 선언하고, 각 연산에 대해 visit 배열을 초기화, 현재 위치와 현재 위치까지 오는 데 걸린 시간을 큐로 선언하고, 이를 넣어준다. eatTime는 해당 물고기를 먹는 데 까지 걸린 시간을 뜻한다.
int cnt = q.front().first;
pair<int, int> cur = make_pair(q.front().second.first, q.front().second.second);
if (arr[cur.first][cur.second] > 0 && arr[cur.first][cur.second] < s && eatTime == cnt) {
if (eat.first > cur.first || (eat.first == cur.first && eat.second > cur.second)) {
eat = cur;
continue;
}
}
q.pop();
이후 여타 bfs와 마찬가지로 현재 시간과 위치를 변수로 빼고 큐에서 pop한다. 그 사이에 물고기를 먹는 규칙에 의해 걸린 시간이 같은데 더 위, 더 왼쪽에 있다면 해당 먹이를 우선으로 먹도록 수정한다.
for (int i = 0; i < 4; i++) {
pair<int, int> ncur = make_pair(cur.first + dy[i], cur.second + dx[i]);
if (ncur.first >= 0 && ncur.first < n && ncur.second >= 0 && ncur.second < n && !visit[ncur.first][ncur.second]){
if (arr[ncur.first][ncur.second] <= s) {
if (arr[ncur.first][ncur.second] > 0 && s > arr[ncur.first][ncur.second] && !isEat) {
isEat = true;
eat = ncur;
eatTime = cnt + 1;
ans += eatTime;
} else {
q.push(make_pair(cnt + 1, make_pair(ncur.first, ncur.second)));
visit[ncur.first][ncur.second] = true;
}
}
}
}
이후 상하좌우를 탐색한다. 우선 배열의 범위를 벗어나지는 않는 지 확인하고, 물고기의 크기를 비교하여 해당 공간을 넘어갈 수 있는 지 확인하고, 물고기를 먹을 수 있는 지 확인한다. 물고기를 먹을 수 있다면 해당 물고기를 먹고 그 위치와 먹는 데 걸린 시간을 초기화해준다. 이 때 물고기를 먹는 시점에서 bfs를 더 진행할 필요가 없으므로 q에 넣지는 않는다.
만약 지나가는 것만 허용된다면 큐에 집어넣어 연산을 계속한다.
if (isEat) {
isEat = false;
eCnt++;
arr[eat.first][eat.second] = 0;
if (eCnt == s) {
s++;
eCnt = 0;
}
} else {
break;
}
만약 위 bfs를 돌면서 물고기를 먹는 데 성공했다면 그 개수를 추가해주고, 먹은 물고기는 없어지므로 배열을 초기화해준다. 먹은 물고기의 개수와 아기 상어의 크기가 같다면 아기 상어의 크기를 증가시켜준다. bfs를 돌았음에도 물고기를 더 먹을 수 없다면 연산을 종료한다.
위 연산이 끝나고 물고기를 먹는 데 걸린 총 시간을 출력하면 정답을 구할 수 있다.
'Coding Test' 카테고리의 다른 글
| 백준 2056 작업 (C++) (0) | 2024.08.01 |
|---|---|
| 백준 3107 IPv6 (C++) (0) | 2024.08.01 |
| 백준 1956 운동 (C++) (0) | 2024.07.29 |
| 백준 1749 점수따먹기 (C++) (0) | 2024.07.28 |
| 백준 1368 물대기 (C++) (1) | 2024.07.27 |