ps

[백준] 2933 - 미네랄

kariskan 2022. 8. 4. 21:44

https://www.acmicpc.net/problem/2933

 

2933번: 미네랄

창영과 상근은 한 동굴을 놓고 소유권을 주장하고 있다. 두 사람은 막대기를 서로에게 던지는 방법을 이용해 누구의 소유인지를 결정하기로 했다. 싸움은 동굴에서 벌어진다. 동굴에는 미네랄

www.acmicpc.net

 

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int n, m;
char map[100][100];

int getLeft(int i) {
	for (int j = 0; j < m; j++) {
		if (map[i][j] == 'x')return j;
	}
	return -1;
}

int getRight(int i) {
	for (int j = m - 1; j >= 0; j--) {
		if (map[i][j] == 'x')return j;
	}
	return -1;
}

int x[4] = { 0,0,1,-1 };
int y[4] = { 1,-1,0,0 };

void go(int r, int c) {

	map[r][c] = '.';
	int visit[100][100] = { 0, }, visit2[100][100] = { 0, };
	int cnt = 0;
    
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (!visit[i][j] && map[i][j] == 'x') {
            
				cnt++;
                
				queue<pair<int, int>> q;
				q.push({ i,j });
				visit[i][j] = cnt;
                
				while (!q.empty()) {
                
					int a = q.front().first;
					int b = q.front().second;
					q.pop();
                    
					for (int k = 0; k < 4; k++) {
						int nx = a + x[k];
						int ny = b + y[k];
						if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visit[nx][ny] && map[nx][ny] == 'x') {
							q.push({ nx,ny });
							visit[nx][ny] = cnt;
						}
					}
				}
			}
		}
	}
    
	vector<int> num;
	vector<vector<pair<int, int>>> q2;
    
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (map[i][j] == 'x' && !visit2[i][j]) {
            
				int minH = -1;
                
				queue<pair<int, int>> q;
				q.push({ i,j });
				visit2[i][j] = 1;
				q2.push_back({});
                
				while (!q.empty()) {
                
					int a = q.front().first;
					int b = q.front().second;
					q.pop();

					q2[q2.size() - 1].push_back({ a,b });
					visit2[a][b] = 1;

					for (int k = a; k < n; k++) {
						if (visit[a][b] == visit[k + 1][b])break;
						if (map[k + 1][b] == 'x' || k == n - 1) {
							if (minH == -1 || minH > k - a) {
								minH = k - a;
							}
							break;
						}
					}

					for (int k = 0; k < 4; k++) {
						int nx = a + x[k];
						int ny = b + y[k];
						if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visit2[nx][ny] && map[nx][ny] == 'x') {
							visit2[nx][ny] = 1;
							q.push({ nx,ny });
						}
					}
				}
				if (minH > 0) {
					sort(q2[q2.size() - 1].begin(), q2[q2.size() - 1].end());
					num.push_back(minH);
				}
				else q2.pop_back();
			}
		}
	}

	for (int i = 0; i < q2.size(); i++) {
		while (!q2[i].empty()) {
			int a = q2[i].back().first;
			int b = q2[i].back().second;
			map[a][b] = '.';
			map[a + num[i]][b] = 'x';
			q2[i].pop_back();
		}
	}
}

int main() {

	cin >> n >> m;
    
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> map[i][j];
		}
	}
    
	int k; cin >> k;
    
	for (int i = 0; i < k; i++) {
    
		int a; cin >> a;
		int l;
			if (i % 2 == 0) { //왼쪽
			l = getLeft(n - a);
		}
		else {
			l = getRight(n - a);
		}
		if (l != -1) {
			go(n - a, l);
		}
	}

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cout << map[i][j];
		}
		cout << '\n';
	}
}

 

 

한 번에 맞긴 했지만 코드를 보면 알 수 있듯이 굉장히 지저분하게 짰다.

그래서 일단 내가 짠 코드를 설명하고 다른 분의 블로그를 참고하여 더 나은 방법을 소개하겠다.

 

getLeft(), getRight()

이 부분은 단순히 해당 높이에서 제거할 미네랄의 좌표를 반환하는 함수이다.

 

go()

이 부분은 미네랄을 제거하고 클러스터를 선별한 뒤 움직이는 부분이다.

 

처음 2중 for문은 bfs를 이용해 미네랄이 있는 위치를 모두 클러스터로 선별하고 각각 번호를 새겼다.

 

다음 2중 for문도 bfs를 이용해 클러스터 별로 미네랄을 모두 전수 조사하며 떨어질 수 있는 높이를 구하였다.

높이를 구하는 과정은 for (int k = a; k < n; k++) 이 부분인데,

같은 클러스터 끼리는 볼 필요가 없으므로 continue를 이용해 주었고,

다른 클러스터를 만나거나 땅을 만날 경우 최솟값으로 갱신해주었다.

최솟값으로 갱신한 이유는 최솟값으로 움직여야 같은 클러스터끼리 딱 맞게 이동하기 때문이다.

 

마지막으로 sort를 한 이유는 항상 아래로 떨어지기 때문에, 밑에 있는 것(i가 더 큰 것)을 먼저 수정하면 위에 있는 것(i가 더 작은 것)이 덮어씌울 수 있기 때문이다.

 

그리고 미리 저장해두었던 q2를 이용해 미네랄을 움직이면 끝이다.

 

..

 

다음은 2JINISHAPPY님의 블로그에서 발췌한 내용이다.

핵심은 땅에서 bfs를 돌려준 뒤, 방문하지 않은 x좌표들을 클러스터로 분류하는 것이었다.

이 과정에서 땅에 . 으로 이루어진 한 줄을 추가한다면 편하게 구현할 수 있다.

 

Reference

https://2jinishappy.tistory.com/171