[BOJ-Code] 16236 - 아기 상어

Posted by DHniyeo Blog on March 29, 2024

문제 링크

💡 너비 우선 탐색/그래프 이론/그래프 탐색/구현/시뮬레이션

Memory 2028KB Time 160ms Code Length 2526B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include<iostream>
#include<math.h>
#include<cstring>
#include<queue>
using namespace std;
int N;
int Map[20][20];
struct info {
	int y, x;
};
info baby_shark, target;
int min_dist = 1e9;
int shark_size = 2;
int get_fish = 0;

void init() {
	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) {
				baby_shark.y = i;
				baby_shark.x = j;
			}
		}
	}
}
int find_dist(int y, int x) { // 상어와 물고기의 최단 거리 구하기.
	// 상어가 큰 물고기를 피해서 이동하는 최단 거리를 구해야 한다.
	const int dy[] = { 1, 0, -1, 0 };
	const int dx[] = { 0,-1, 0, 1 };
	queue<info> q;
	int visited[20][20];
	memset(visited, 0, sizeof(visited));

	q.push({ baby_shark.y, baby_shark.x });
	visited[baby_shark.y][baby_shark.x] = 1;
	while (!q.empty()) {
		info now = q.front(); q.pop();
		if (now.y == y && now.x == x) { // 해당 물고기 위치에 도달
			return visited[now.y][now.x] - 1;
		}
		for (int i = 0; i < 4; i++) {
			int ny = now.y + dy[i];
			int nx = now.x + dx[i];
			if (ny >= N || nx >= N || ny < 0 || nx < 0) continue;
			if (visited[ny][nx] != 0) continue;
			if (Map[ny][nx] > shark_size) continue; // 큰 물고기 피해가기
			visited[ny][nx] = visited[now.y][now.x] + 1;
			q.push({ ny,nx });
		}
	}
	return 1e9; // 도달 하지 못했으므로 매우 큰값 반환
}

bool findFish() {
	bool flag = false;
	min_dist = 1e9;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (Map[i][j] < shark_size && Map[i][j] > 0 && Map[i][j] != 9) { // 물고기 크기가 작고 거리가 최소일때
				// 최단 거리 구하기.
				int dist = find_dist(i, j);
				if (min_dist > dist) {
					flag = true;
					min_dist = dist;
					target.y = i;
					target.x = j;
				}
			}
		}
	}
	return flag;
}
void eat_fish() {
	Map[baby_shark.y][baby_shark.x] = 0; // 현재 위치 비움
	Map[target.y][target.x] = 9; // 물고기 위치가 상어 위치가됨.
	baby_shark.y = target.y;
	baby_shark.x = target.x;
	get_fish++; // 먹은 물고기 수 증가
	if (get_fish == shark_size) { // 상어가 먹은 물고기가 조건에 부합할 시 크기 성장.
		shark_size++;
		get_fish = 0;
	}
}
void printmap() { // 디버깅용
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cout << Map[i][j] << " ";
		}
		cout << endl;
	}
}

int main() {
	init();
	int cnt = 0;
	while (1) {
		if (!findFish()) { // 물고기 찾기
			break;
		}
		eat_fish();
		cnt += min_dist;
	}
	cout << cnt;
}

이 코드는 N x N 크기의 맵에서 상어가 먹을 수 있는 물고기를 찾아 먹는 프로그램이다.

init 함수에서 맵을 초기화하고 상어의 위치를 찾는다.

find_dist 함수는 상어와 물고기 사이의 최단 거리를 구하는 함수이다.

findFish 함수는 상어가 먹을 수 있는 물고기를 찾고, 그 중 최단 거리에 있는 물고기를 선택한다.

eat_fish 함수는 선택된 물고기를 먹는다. 상어의 위치를 업데이트하고, 먹은 물고기 수가 상어 크기와 같아지면 상어 크기를 키운다.

main 함수에서는 물고기를 찾아 먹는 과정을 반복하며 걸린 시간을 출력한다.


Behind..

처음에 이 문제를 풀 때 **자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없다!!** 라는 조건을 보지 못했고 아래와 같이 코딩을 했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include<iostream>
#include<math.h>
using namespace std;
int N;
int Map[20][20];
struct info {
	int y, x;
};
info baby_shark, target;
int min_dist = 1e9;
int shark_size = 2;
int get_fish = 0;

void init() {
	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) {
				baby_shark.y = i;
				baby_shark.x = j;
			}
		}
	}
}
bool findFish() {
	bool flag = false;
	min_dist = 1e9;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (Map[i][j] < shark_size && Map[i][j] > 0) { // 물고기 크기가 작고 거리가 최소일때
				flag = true;
				int dist = abs(baby_shark.y - i) + abs(baby_shark.x - j);
				if (min_dist > dist) {
					min_dist = dist;
					target.y = i;
					target.x = j;
				}
			}
		}
	}
	return flag;
}
void eat_fish() {
	Map[baby_shark.y][baby_shark.x] = 0; // 현재 위치 비움
	Map[target.y][target.x] = 9; // 물고기 위치가 상어 위치가됨.
	baby_shark.y = target.y;
	baby_shark.x = target.x;
	get_fish++; // 먹은 물고기 수 증가
	if (get_fish == shark_size) { // 상어가 먹은 물고기가 조건에 부합할 시 크기 성장.
		shark_size++;
		get_fish = 0;
	}
}
void printmap() { // 디버깅용
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cout << Map[i][j] << " ";
		}
		cout << endl;
	}
}

int main() {
	init();
	int cnt = 0;
	while (1) {
		if (!findFish()) { // 물고기 찾기
			break;
		}
		eat_fish();
		cnt += min_dist;
	}
	printf("%d\n", cnt);
}
<반례> ```c++ 6 6 0 6 0 6 1 0 0 0 0 0 2 2 3 4 5 6 6 0 0 0 0 0 2 0 2 0 0 0 0 3 9 3 0 0 1 ``` 그래서 BFS를 이용해서 각 상어까지 도달하는데 걸리는 거리를 계산하는 함수를 추가하였다. ```c++ #include #include #include #include using namespace std; int N; int Map[20][20]; struct info { int y, x; }; info baby_shark, target; int min_dist = 1e9; int shark_size = 2; int get_fish = 0; void init() { 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) { baby_shark.y = i; baby_shark.x = j; } } } } int find_dist(int y, int x) { // 상어와 물고기의 최단 거리 구하기. // 상어가 큰 물고기를 피해서 이동하는 최단 거리를 구해야 한다. const int dy[] = { 1, 0, -1, 0 }; const int dx[] = { 0,-1, 0, 1 }; queue q; int visited[20][20]; memset(visited, 0, sizeof(visited)); q.push({ baby_shark.y, baby_shark.x }); visited[baby_shark.y][baby_shark.x] = 1; while (!q.empty()) { info now = q.front(); q.pop(); if (now.y == y && now.x == x) { // 해당 물고기 위치에 도달 return visited[now.y][now.x]-1; } for (int i = 0; i < 4; i++) { int ny = now.y + dy[i]; int nx = now.x + dx[i]; if (ny >= N || nx >= N || ny < 0 || nx < 0) continue; if (visited[ny][nx] != 0) continue; if (Map[ny][nx] > shark_size) continue; // 큰 물고기 피해가기 visited[ny][nx] = visited[now.y][now.x] + 1; q.push({ ny,nx }); } } return 1e9; // 도달 하지 못했으므로 매우 큰값 반환 } bool findFish() { bool flag = false; min_dist = 1e9; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (Map[i][j] < shark_size && Map[i][j] > 0) { // 물고기 크기가 작고 거리가 최소일때 // 최단 거리 구하기. int dist = find_dist(i, j); if (min_dist > dist) { flag = true; min_dist = dist; target.y = i; target.x = j; } } } } return flag; } void eat_fish() { Map[baby_shark.y][baby_shark.x] = 0; // 현재 위치 비움 Map[target.y][target.x] = 9; // 물고기 위치가 상어 위치가됨. baby_shark.y = target.y; baby_shark.x = target.x; get_fish++; // 먹은 물고기 수 증가 if (get_fish == shark_size) { // 상어가 먹은 물고기가 조건에 부합할 시 크기 성장. shark_size++; get_fish = 0; } } void printmap() { // 디버깅용 for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { cout << Map[i][j] << " "; } cout << endl; } } int main() { init(); int cnt = 0; while (1) { if (!findFish()) { // 물고기 찾기 break; } eat_fish(); cnt += min_dist; } cout << cnt; } ``` 결국 최종적으로 수정한 코드가 맨 위에 있는 코드이다.. 이 문제를 푼 것에 만족을 했지만 다른 코드들을 확인해 보니 시간을 훨씬 더 줄일 수 있는 방법이 있었다. 바로, 물고기들의 위치를 모든 배열을 돌며 일일이 먹을 수 있는 물고기를 찾은 후 물고기와 상어의 거리를 각각 측정해 최솟값을 찾는 것이 아닌, 상어의 위치에서 bfs로 주변을 탐색하고, 탐색된 것이 먹을 수 있는 물고기라면 해당 물고기에 대한 데이터를 찾는 방법을 쓰면 좀 더 시간을 줄일 수 있었다. 이때 먹을 수 있는 물고기가 여러 마리라면 맨 위, 그리고 맨 왼쪽에 있는 물고기 순서로 우선순위를 가지므로, Priority Queue를 이용한 BFS를 작성할 수 있다. ```c++ #define _CRT_SECURE_NO_WARNINGS #include #include using namespace std; int n; int map[21][21]; struct Shark { int y, x, size; int eat; // 지금까지 먹은 물고기 개수 }; struct Fish { int y, x, lev; }; Shark shark; int dy[4] = { -1,0,0,1 }; int dx[4] = { 0,-1,1,0 }; struct Compare { bool operator() ( Fish f, Fish tar ) { if (tar.lev < f.lev) return true; else if (tar.lev > f.lev) return false; if (tar.y < f.y) return true; else if (tar.y > f.y) return false; return tar.x < f.x; } }; Fish bfs() { Fish f = { -1,-1,-1 }; priority_queue<Fish, vector, Compare> q; int visited[21][21] = { 0, }; q.push({ shark.y, shark.x }); visited[shark.y][shark.x] = 1; while (!q.empty()) { Fish now = q.top(); q.pop(); if (1 <= map[now.y][now.x] && map[now.y][now.x] < shark.size) return now; for (int i = 0; i < 4; i++) { int ny = now.y + dy[i]; int nx = now.x + dx[i]; if (ny < 0 || ny >= n || nx < 0 || nx >= n) continue; if (visited[ny][nx] || map[ny][nx] > shark.size) continue; q.push({ ny,nx,now.lev + 1 }); visited[ny][nx] = 1; } } return f; } int solve() { int t = 0; while (1) { // 1. bfs Fish ret = bfs(); if (ret.y == -1) return t; // 2. 물고기 먹기 map[ret.y][ret.x] = 0; shark.y = ret.y, shark.x = ret.x; if (++shark.eat == shark.size) { shark.size++; shark.eat = 0; } t += ret.lev; } return t; } int main() { //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) { map[i][j] = 0; shark = { i,j,2,0 }; } } } int ans = solve(); printf("%d\n", ans); return 0; } ``` --- 2024-8-21일 다시 이 문제를 풀어 보았다. 조금 오래 걸리긴 했지만 빡구현 문제라 그리 어렵진 않았던 것 같다. > **Memory 2028KB Time 156ms Code Length 2059B** ```c++ #include #include using namespace std; int N; int map[20][20]; struct shark_info { int y, x, ate, size; }; struct target_info { int y, x, dist; }; struct queue_info { int y, x, dist; }; shark_info shark; target_info fish; int dist = 0; void init() { 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.ate = 0; shark.size = 2; } } } } int bfs(int target_y, int target_x) { const int dy[] = {-1,1,0,0}; const int dx[] = {0,0,-1,1}; int dist = 1e9; int visited[20][20] = { 0 }; queue q; q.push({ shark.y,shark.x,0 }); while (!q.empty()) { queue_info now = q.front(); q.pop(); if (now.y == target_y && now.x == target_x) { dist = now.dist; break; } for (int i = 0; i < 4; i++) { int ny = now.y + dy[i]; int nx = now.x + dx[i]; if (ny >= N || nx >= N || ny < 0 || nx < 0) continue; if (map[ny][nx] > shark.size) continue; if (visited[ny][nx] == 1) continue; visited[ny][nx] = 1; q.push({ ny,nx,now.dist + 1 }); } } return dist; } bool find_fish() { // 경로상 먹을 수 있는 물고기 찾기 (fish data 최신화) int minV = 1e9; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (map[i][j] != 0 && map[i][j] != 9 && map[i][j] < shark.size) { int dist = bfs(i, j); if (dist < minV) { // 최솟값 갱신 minV = dist; fish.y = i; fish.x = j; fish.dist = minV; } } } } if (minV == 1e9) { return false; } else { return true; } } void eat_fish() { // 맵 최신화 map[shark.y][shark.x] = 0; map[fish.y][fish.x] = 9; // shark 위치 이동 shark.y = fish.y; shark.x = fish.x; shark.ate++; if (shark.ate == shark.size) { // 상어 크기 반영 shark.ate = 0; shark.size++; } } int main() { init(); int T =0; while (1) { if (!find_fish()) { // 먹을수 있는 물고기 존재 x break; } eat_fish(); T += fish.dist; } cout << T; } ``` 이 코드는 N x N 크기의 맵에서 상어가 먹을 수 있는 물고기를 찾아가며 먹는 과정을 시뮬레이션하는 프로그램이다. init() 함수에서 맵의 크기(N)와 각 칸의 정보를 입력받고, 상어의 초기 위치와 크기를 설정한다. bfs() 함수는 상어의 위치부터 목표 지점까지의 최단 거리를 계산하는 함수이다. 너비 우선 탐색을 이용하여 최단 거리를 구한다. find_fish() 함수는 상어가 먹을 수 있는 물고기를 찾아서 그 물고기까지의 최단 거리를 구하는 함수이다. 이때, 상어의 크기보다 작은 물고기를 찾아야 하며, 최단 거리를 가진 물고기를 찾아 fish 구조체에 저장한다. eat_fish() 함수는 먹을 물고기가 정해지면 해당 물고기를 먹고 상어의 위치와 크기를 업데이트하는 함수이다. main() 함수에서는 init() 함수를 호출하여 초기화를 수행하고, find_fish() 함수를 통해 먹을 수 있는 물고기를 찾아가며 eat_fish() 함수를 호출하여 물고기를 먹는 과정을 반복한다. 이를 통해 총 소요된 시간(T)을 계산하여 출력한다.