ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 2933 미네랄
    Problem Solving/Baekjoon Online Judge 2020. 6. 1. 16:22

    ✔️ 문제 링크

    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에 저장해두고, 떨어뜨리는 방식을 적용했다. 

    구현력이 아직 많이 부족하다고 느낀 문제였다. 

     

    'Problem Solving > Baekjoon Online Judge' 카테고리의 다른 글

    [백준] 10282 해킹  (0) 2020.06.02
    [백준] 16938 캠프 준비  (0) 2020.06.01
    [백준] 16932 모양 만들기  (0) 2020.05.29
    [백준] 2531 회전초밥  (0) 2020.05.27
    [백준] 1664 소수의 연속합  (1) 2020.05.19

    댓글

Designed by Tistory.