[백준] 2933 미네랄
✔️ 문제 링크
https://www.acmicpc.net/problem/2933
2933번: 미네랄
창영과 상근은 한 동굴을 놓고 소유권을 주장하고 있다. 두 사람은 막대기를 서로에게 던지는 방법을 이용해 누구의 소유인지를 결정하기로 했다. 싸움은 동굴에서 벌어진다. 동굴에는 미네랄��
www.acmicpc.net
✔️ 문제 이해
◾ RxC 크기의 동굴에 미네랄이 존재한다. ( '.'는 빈 칸, 'x'는 미네랄을 나타낸다. )
◾ 높이 h에서 막대기를 던지면 땅과 수평을 이루며 날아간다. ( 왼쪽, 오른쪽에서 번갈아 던진다.)
◾ 이때 막대기가 미네랄에 부디치면, 미네랄은 부숴진다.
◾ 미네랄은 클러스터를 이루고 있다. 클러스터란 인접한 미네랄들의 집합을 뜻한다.
◾ 막대기를 던져서 미네랄이 부숴지면, 클러스터가 분리 될 수 있다.
◾ 이때 새롭게 생성된 클러스터가 공중에 떠있는 경우 중력에 의해 바닥으로 떨어진다.
◾ 클러스터는 땅에 닿거나 다른 클러스터에 닿을때까지 떨어진다.
◾ 동굴에 있는 미네랄들의 정보와, 던져지는 막대기들의 위치 정보가 입력으로 주어진다.
◾ 모든 막대를 던지고 난 이후의 미네랄 모양을 출력하면 된다.
✔️ 설계
◾ 왼쪽, 오른쪽에서 막대기를 던져 미네랄을 부순다.
◾ 공중에 있는 클러스터가 존재하면 중력을 작용한다.
◾ 클러스터는 bfs 또는 dfs로 구분 짓는다.
◾ 땅에 닿아있는 클러스터 정보를 저장해두어 활용한다.
👨🏻💻 소스 코드
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef struct {
int x, y;
}pos;
char map[100][100];
int visited[100][100];
int r, c, n, cnt = 1;
int stick[100], bottomCluster[101];
int dx[] = { -1, 1, 0, 0 }, dy[] = { 0,0,-1,1 };
// dfs을 사용하여 visited 배열에 번호로 클러스터를 체크한다.
void dfs(int x, int y) {
if (!bottomCluster[cnt] && x == r - 1) // 땅에 닿아있는 클러스터를 bottomCluster 배열에 체크.
bottomCluster[cnt] = true;
visited[x][y] = cnt;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= r || nx < 0 || ny >= c || ny < 0) continue;
if (visited[nx][ny]) continue;
if (map[nx][ny] == '.') continue;
dfs(nx, ny);
}
}
void fallMineral() { // 클러스터를 떨어뜨린다.
cnt = 1;
memset(visited, 0, sizeof(visited));
memset(bottomCluster, 0, sizeof(bottomCluster));
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
if (map[i][j] == 'x' && !visited[i][j]) {
dfs(i, j); // dfs로 각 클러스터를 구분 짓는다.
cnt++;
}
}
}
// 공중에 떠 있는 클러스터가 있는지 확인한다.
// (땅에 닿지 않아있는 클러스터 체크.)
bool flag = false;
for (int i = 1; i < cnt; i++) {
if (bottomCluster[i] == false) {
flag = true;
}
}
if (flag) { // 공중에 떠있는 생긴경우. 중력을 작용한다.
vector<pos> v; // 떨어뜨릴 클러스트의 좌표를 저장해둔다.
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
if (visited[i][j] != 0 && !bottomCluster[visited[i][j]]) {
v.push_back({ i,j });
}
}
}
bool flag2 = false;
while (1) {
for (int i = 0; i < v.size(); i++) { // 공중에 있는 클러스터를 우선 map에서 지운다.
map[v[i].x][v[i].y] = '.';
}
for (int i = 0; i < v.size(); i++) {
int nx = v[i].x + 1, ny = v[i].y; // 한 칸 아래 좌표 위치 계산.
v[i].x = nx, v[i].y = ny; // 좌표 업데이트.
map[nx][ny] = 'x'; // 배치.
// 클러스터가 땅에 닿는 경우
if (nx == r - 1) {
flag2 = true;
} // 클러스터가 다른 클러스터에 닿는 경우
else if (map[nx + 1][ny] == 'x' && (visited[v[i].x][v[i].y] != visited[nx + 1][ny])) {
flag2 = true;
}
}
if (flag2) return; // 클러스터가 땅에 닿거나 다른 클러스터에 닿으면 탈출.
}
}
}
// flag에 따라 왼쪽, 오른쪽을 구분한다.
// h 높이에서 막대기를 던져 미네랄을 부순다.
void breakMineral(bool flag, int h) {
h = r - h;
if (flag) { // 왼쪽 h 높이에서 막대기를 던져 미네랄을 부숨.
for (int col = 0; col < c; col++) {
if (map[h][col] == 'x') {
map[h][col] = '.';
return;
}
}
}
else { // 오른쪽 h 높이에서 막대기를 던져 미네랄을 부숨.
for (int col = c - 1; col >= 0; col--) {
if (map[h][col] == 'x') {
map[h][col] = '.';
return;
}
}
}
}
void solve() {
bool flag = true;
for (int i = 0; i < n; i++) {
// 왼쪽에서 던지고, 오른쪽에서 던지고 반복.
breakMineral(flag, stick[i]);
if (flag) flag = false; // flag는 왼쪽, 오른쪽을 표현한다.
else flag = true;
fallMineral(); // 미네랄을 떨어뜨린다.
}
for (int i = 0; i < r; i++) { // 결과 출력.
for (int j = 0; j < c; j++) {
cout << map[i][j];
}
cout << endl;
}
}
int main() {
cin >> r >> c; // map 정보 입력.
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
cin >> map[i][j];
}
}
cin >> n; // 막대기 투척 정보 입력.
for (int i = 0; i < n; i++) {
cin >> stick[i];
}
solve();
return 0;
}
✔️ 문제 회고
◾ bfs/dfs을 활용한 시뮬레이션 문제였다.
◾ 클러스터에 중력을 작용하는 부분을 구현할때 쉽지 않았다.
◾ 떨어뜨려야할 클러스터의 좌표를 vector에 저장해두고, 떨어뜨리는 방식을 적용했다.
◾ 구현력이 아직 많이 부족하다고 느낀 문제였다.