반응형
리뷰
일반적인 BFS를 사용하니 바로 시간 초과가 출력되었다!!
벽을 의미하는 1을 보기 보단, 0부터 본 후에 1을 체크하는 방식이 효율적인 문제였다.
위에서부터 차례대로 group정보를 배열, 벡터, unordered_map을 사용하여 관리한 정보이다.
배열이 시간은 가장 빠르고 벡터가 메모리 낭비가 덜 심하고 해시는 그닥 효율이 좋지 못했다.
따라서 해당 문제는 벡터를 활용한 방법으로 설명하겠다.
https://www.acmicpc.net/problem/16946
문제 풀이
전역변수
- lst : 맵을 저장할 문자열 벡터
- group : 그룹의 면적을 저장할 벡터, 인덱싱을 위해 크기를 1을 가진 상태로 시작한다.
- groups : 그룹의 면적을 2D맵에 나타내기 위한 배열
- v : 방문을 체크하기 위한 배열, 사실상 groups 배열하나로 관리가 가능하다.
- dx, dy : 4방향 탐색을 하기 위한 배열
- n, m : 행의 크기 n, 열의 크기 m
- Pos : 2차원 탐색을 위한 x, y 좌표를 나타내기 위한 구조체
- n과 m값을 입력 받고 맵 크기를 n으로 맞추어 준 뒤 맵 정보를 문자열로 받아와 준다.
- 그룹은 1부터 시작할 것이기 때문에 index를 1로 맞추어 주고 2차원 배열 탐색을 시작한다.
- 만약 맵상에서 0을 만났고, 해당 좌표가 방문처리 되어있지 않다면 floodfill 함수를 실행해 주고 index를 증가시킨다.
- floodfill 함수의 매개 변수는 탐색을 시작할 좌표 sx, sy와 현재 그룹을 묶을 key(index)이다.
- 현재를 방문처리, groups에는 key를 기록해 주고 cnt는 1개로 설정해 준 뒤 큐에 좌표를 삽입한다.
- 큐가 빌때까지 탐색을 진행하고 방문하지 않았고, 맵상 0인 모든 곳을 방문처리 및 groups에 key를 기록해 준다.
- group벡터에 현재 그룹의 넓이 cnt를 push해준다. 이는 나중에 groups에서 인덱스로 파싱이 가능하게끔 하는 것이다.
- 맵상에서 모든 0에 대해 그루핑이 끝났다면 이제 출력을 할 일만 남았다.
- 맵상에서 0을 만났다면 0을 출력해 주고, 1을 만난 경우엔 chk 함수를 통해 탐색을 추가로 시작한다.
- chk의 매개변수로는 탐색을 시작할 좌표이고 벽을 허문 상태기에 cnt를 1로 초기화 해준다.
- 중복 제거를 위해 set를 사용해 준다. 오름 차순은 의미 없기에 unordered_set를 사용해 주면 된다. keys로 초기화
- 4방향을 탐색하고 해당 위치가 범위 내면서 벽이 아니고 그룹이 존재한다면 keys에 해당 그룹을 추가해 준다.
- keys에 저장된 값을 group의 인덱스로 매핑하여 cnt에 추가해 준뒤 cnt를 리턴해 준다.
- 리턴 받은 값을 10으로 나눈 나머지를 출력해 주면 된다. 해당 로직을 n * m범위 모두 진행해 주면 된다.
참고 사항
set을 통해 중복을 제거해 주지 않으면 같은 그룹일지라도 중복하여 더하게 된다.
위에서도 언급했지만 방문 배열 대신 groups를 사용해도 된다, 대신 벽인 경우만 if문으로 잘 예외처리 해줘야 한다.
정답 코드
#include<iostream>
#include<queue>
#include<unordered_set>
using namespace std;
vector<string> lst;
vector<int> group(1);
int groups[1000][1000] = { 0, };
int v[1000][1000] = { 0, };
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
int n, m;
struct Pos {
int x, y;
};
int chk(int sx, int sy) {
int cnt = 1;
unordered_set<int> keys;
for (int i = 0; i < 4; i++) {
int nx = sx + dx[i], ny = sy + dy[i];
if (0 <= nx && nx < n && 0 <= ny && ny < m && lst[nx][ny] != '1' && groups[nx][ny]) keys.insert(groups[nx][ny]);
}
for (int key : keys) cnt += group[key];
return cnt;
}
void floodfill(int sx, int sy, int key) {
queue<Pos> q;
q.push({ sx, sy });
int cnt = 1;
v[sx][sy] = 1;
groups[sx][sy] = key;
while (!q.empty()) {
Pos pos = q.front(); q.pop();
int cx = pos.x, cy = pos.y;
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] && lst[nx][ny] == '0') {
v[nx][ny] = 1;
groups[nx][ny] = key;
cnt++;
q.push({ nx , ny });
}
}
}
group.push_back(cnt);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
lst.resize(n);
for (int i = 0; i < n; i++) cin >> lst[i];
int index = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (lst[i][j] == '0' && !v[i][j]) floodfill(i, j, index++);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (lst[i][j] == '0') cout << 0;
else cout << chk(i, j) % 10;
}
cout << "\n";
}
}
728x90
반응형
'알고리즘 공부 > C++' 카테고리의 다른 글
[P5] 백준 2887번 행성 터널 C++ MST, 최소 신장 트리 (0) | 2024.09.19 |
---|---|
[G1] 백준 13460번 구슬 탈출 2 C++ BFS, 구현, 시뮬레이션, 너비 우선 탐색 (2) | 2024.09.18 |
[G5] 백준 2166번 다각형의 면적 C++ 기하학, 신발끈 공식, 슈메르카우스키의 공식 (2) | 2024.09.16 |
[G2] 백준 1400번 화물차 C++ 다익스트라, 최단 경로, 구현 (6) | 2024.09.16 |
[G4] 백준 7662번 이중 우선순위 큐 C++ 멀티셋, multiset (0) | 2024.09.16 |