개인사
[G3] 백준 15573번 채굴 C++ 너비 우선 탐색, 매개 변수 탐색 본문
728x90

리뷰

시간이 생각보다 느려서 최적해인 방법은 아닌듯 하다.
전역 변수
- N : 배열의 최대 크기를 정의할 상수 변수
- M : 맵 정보를 저장할 2차원 배열
- v : 방문 정보를 저장할 2차원 배열
- n, m : 맵의 세로/가로 크기를 저장할 변수
- k : 채굴할 광물의 최소 개수를 저장할 변수
- Pos : 위지 정보를 정의할 구조체
- dx, dy : 4방향 탐색을 위한 방향 배열
함수
1. init
int init(queue<Pos>& q, int d) {
int cnt = 0;
for (int i = 0; i < n; ++i) {
v[i][0] = true;
if (M[i][0] <= d) {
++cnt;
q.push({ i, 0 });
}
if (m == 1) continue;
v[i][m - 1] = true;
if (M[i][m - 1] <= d) {
++cnt;
q.push({ i, m - 1 });
}
}
for (int j = 1; j < m - 1; ++j) {
v[0][j] = true;
if (M[0][j] <= d) {
++cnt;
q.push({ 0, j });
}
}
return cnt;
}
초기 공기와 맞닿은 광물을 확인하고 채굴이 가능할 경우 큐에 삽입하기 위한 함수
2. bfs
bool bfs(int d) {
memset(v, 0, sizeof(v));
queue<Pos> q;
int cnt = init(q, d);
while (!q.empty()) {
Pos pos = q.front(); q.pop();
int cx = pos.x, cy = pos.y;
if (cnt >= k) return true;
for (int i = 0; i < 4; ++i) {
int nx = cx + dx[i], ny = cy + dy[i];
if (0 <= nx && nx < n && 0 <= ny && ny < m && !v[nx][ny]) {
v[nx][ny] = true;
if (M[nx][ny] <= d) {
++cnt;
q.push({ nx, ny });
}
}
}
}
return false;
}
d의 강도로 k개 이상의 광물을 채굴할 수 있는지 여부를 반환하기 위한 함수
문제풀이
- n, m, k값을 입력 받고, 변수 mx를 0으로 초기화한다.
- n*m크기의 맵을 초기화하고, 가장 큰 광물의 강도를 mx에 저장한다.
- 변수 l을 1로, r을 mx로, d를 mx로 초기화한다.
- l이 r이하일 경우를 조건으로 하는 while루프를 수행한다.
- 변수 mid를 l+r을 2로 나눈 몫으로 저장한다.
- bfs함수에 mid를 매개변수로 전달하고, mid강도로 k개 이상의 광물을 채굴할 수 있는지를 확인한다.
- bfs함수의 반환값이 true일 경우 d를 mid로 저장하고, r을 mid-1로 저장한다.
- bfs함수의 반환값이 false일 경우 l을 mid+1로 저장한다.
- while루프가 종료된 후 d에 저장된 값을 출력한다.
트러블 슈팅
- 이분 탐색을 수행할때 while루프의 조건을 l<r로 설정하여 WA를 받았다.
- while루프의 조건을 l<=r로 수정하여 AC를 받았다.
참고 사항
- 공기와 맞닿는 광물을 미리 큐에 넣어두고 매번 복사해서 사용하려 했으나 구현이 복잡해 질 것 같아 포기했다.
- 조기 종료를 처리하기 위해 구조체 복사 대신 pair<int, int>사용, memset대신 int기반의 방문 처리, init함수에서 cnt가 k이상이 되었을 경우 조기 종료 등 가지치기를 이것저것 추가해보았으나 오히려 시간 복잡도가 더 늘어났다.
- 아무래도 광물의 강도가 최대 백만이라 위 가지치기보단 bfs함수를 효율적으로 수행하는 게 더 효과가 있는 듯 하다.
정답 코드
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 1000;
int M[N][N];
bool v[N][N];
int n, m, k;
struct Pos {
int x, y;
};
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
int init(queue<Pos>& q, int d) {
int cnt = 0;
for (int i = 0; i < n; ++i) {
v[i][0] = true;
if (M[i][0] <= d) {
++cnt;
q.push({ i, 0 });
}
if (m == 1) continue;
v[i][m - 1] = true;
if (M[i][m - 1] <= d) {
++cnt;
q.push({ i, m - 1 });
}
}
for (int j = 1; j < m - 1; ++j) {
v[0][j] = true;
if (M[0][j] <= d) {
++cnt;
q.push({ 0, j });
}
}
return cnt;
}
bool bfs(int d) {
memset(v, 0, sizeof(v));
queue<Pos> q;
int cnt = init(q, d);
while (!q.empty()) {
Pos pos = q.front(); q.pop();
int cx = pos.x, cy = pos.y;
if (cnt >= k) return true;
for (int i = 0; i < 4; ++i) {
int nx = cx + dx[i], ny = cy + dy[i];
if (0 <= nx && nx < n && 0 <= ny && ny < m && !v[nx][ny]) {
v[nx][ny] = true;
if (M[nx][ny] <= d) {
++cnt;
q.push({ nx, ny });
}
}
}
}
return false;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> k;
int mx = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> M[i][j];
if (mx < M[i][j]) mx = M[i][j];
}
}
int l = 1, r = mx, d = mx;
while (l <= r) {
int mid = (l + r) / 2;
if (bfs(mid)) {
d = mid;
r = mid - 1;
}
else l = mid + 1;
}
cout << d;
}
728x90
'알고리즘 공부 > C++' 카테고리의 다른 글
| [G3] 백준 1613번 역사 C++ 플로이드 와샬, 최단 경로 (0) | 2025.12.05 |
|---|---|
| [C++] std::rand, std::srand, random 헤더 (0) | 2025.12.04 |
| [G3] 백준 23326번 홍익 투어리스트 C++ 이분 탐색, 집합, set, lower_bound (0) | 2025.12.02 |
| [G4] 백준 10216번 Count Circle Groups C++ 너비 우선 탐색, 기하학, BFS (0) | 2025.12.01 |
| [G4] 백준 5913번 준규와 사과 C++ 백트래킹 (0) | 2025.11.29 |
