BOJ 2933

미네랄

문제출처

문제 해석

  • 창영과 상근이 번갈아가면서 막대기를 던졌을 때, 미네랄의 상태를 출력하는 문제입니다.
  • RxC칸으로 이루어져있으며, 인접한 미네랄 덩어리를 클러스터라고 부릅니다.
  • 정해진 높이에 막대기가 날아가다가 미네랄을 만나면 해당 칸의 미네랄은 파괴됩니다.
  • 미네랄이 파괴된 이후에 남은 클러스터가 분리될 경우, 공중에 떠있으면 중력에 의해 떨어지게 됩니다.
  • 막대기를 왼쪽과 오른쪽에서 번갈아가면서 던집니다.

설계(20)

  • 여기서 가장 중요한 점은 미네랄이 파괴되고 클러스터가 분리되는지 확인하는 것입니다.
  • 미네랄이 파괴되고 4방향에 대해 dfs탐색을 진행합니다.
  • 각 좌표정보가 들어있는 cluster 벡터에 탐색을 진행하면서 넣게 됩니다.
  • 해당 클러스터가 바닥에 있는지 체크 후, 바닥에 있지 않으면 저장되어있는 클러스터에 대해서 바닥과 닿게 만듭니다.

구현(50)

코드
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define MAXN 100

char map[MAXN][MAXN];
int R, C, N;
int dx[] = { 0,0,-1,1 };
int dy[] = { 1,-1,0,0 };
bool visited[MAXN][MAXN];
bool is_range(int x, int y) {
	return (x >= 0 && y >= 0 && x < R && y < C);
}
int max_x;
bool flag;
vector<pair<int, int> > v;
void dfs(int x, int y) {
	if (x == R - 1) {
		flag = true;
	}
	v.push_back({ x,y });
	visited[x][y] = true;
	for (int k = 0; k < 4; k++) {
		int nx = x + dx[k];
		int ny = y + dy[k];
		if (!is_range(nx, ny)) continue;
		if (visited[nx][ny]) continue;
		if (map[nx][ny] == 'x') dfs(nx, ny);
	}
}

void drop(vector<pair<int,int> >& cluster) {
	bool possible = true;
	while (possible) {
		for (int i = 0; i < cluster.size(); i++) {
			map[cluster[i].first][cluster[i].second] = '.';
		}
		for (int i = 0; i < cluster.size(); i++) {
			int tmp_x = cluster[i].first;
			tmp_x++;
			if (map[tmp_x][cluster[i].second] == 'x' || tmp_x == R) {
				possible = false;
				break;
			}
		}
		if (possible) {
			for (int i = 0; i < cluster.size(); i++) {
				cluster[i].first += 1;
				map[cluster[i].first][cluster[i].second] = 'x';
			}
		}
		else {
			for (int i = 0; i < cluster.size(); i++) {
				map[cluster[i].first][cluster[i].second] = 'x';
			}
		}
	}

}
void go(int idx, int i) {
	memset(visited, false, sizeof(visited));
	//왼쪽부터
	if (idx % 2 == 0) {
		for (int j = 0; j < C; j++) {
			if (map[i][j] == 'x') {
				map[i][j] = '.';
				
				for (int k = 0; k < 4; k++) {
					if ((idx + 1) % 2 == k) continue;
					int nx = i + dx[k];
					int ny = j + dy[k];
					if (!is_range(nx, ny)) continue;
					if (visited[nx][ny] || map[nx][ny] == '.') continue;
					flag = false;
					v.clear();
					dfs(nx, ny);
					if (!flag) {
						drop(v);
					}
				}
				break;
			}
		}

	}
	else {
		for (int j = C - 1; j >= 0; j--) {
			if (map[i][j] == 'x') {
				map[i][j] = '.';
				for (int k = 0; k < 4; k++) {
					if ((idx + 1) % 2 == k) continue;
					int nx = i + dx[k];
					int ny = j + dy[k];
					if (!is_range(nx, ny)) continue;
					if (visited[nx][ny]||map[nx][ny]=='.') continue;
					flag = false;
					v.clear();
					dfs(nx, ny);
					if (!flag) {
						drop(v);
					}
				}
				break;
			}
		}

	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> R >> C;
	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++) {
		int idx;
		cin >> idx;
		idx = R - idx;
		
		go(i, idx);
	}
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			cout << map[i][j];
		}
		cout << "\n";
	}
	return 0;
}
디버깅
  • 처음에 dfs탐색할 때 바닥에 있는지 여부를 체크하지 않고 바닥 또는 클러스터와의 거리 최솟값을 찾게 했습니다.
  • 구현결과 WA가 나와서 단순히 바닥에 닿는지만 체크하도록 변경하였고, cluster벡터변수에 저장되어있는 좌표를 떨어뜨리게 재구현하였습니다.
제출결과
  • 15ms

마무리

  • dfs 또는 bfs를 사용하는 문제로, cluster를 찾고 공중에 떠있는지 확인한 다음 떨어뜨리는 시뮬레이션을 구현하는 문제였습니다. 구현문제를 풀이할 때 시간이 너무 오래걸려서 더 연습이 필요할 것 같습니다.