ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 13460 구슬 탈출 2
    Problem Solving/Baekjoon Online Judge 2020. 3. 6. 19:23

    [문제이해]
    1. 맵에는 벽(#), 빨간구슬(R), 파란구슬(B), 구멍(O)이 각각 1개씩 존재한다.
    2. 4가지 기울이기(왼쪽, 오른쪽, 위쪽, 아래쪽)를 통해 구슬을 굴릴 수 있다.
    - 빨간구슬이 구멍에 빠지면 성공
    - 파란구슬이 구멍에 빠지면 실패
    - 빨간구슬과 파란구슬 모두 구멍에 빠져도 실패
    - 기울이기를 10번 초과하면 실패
    3. 보드의 상태가 주어졌을 때 최소 몇 번만에 빨간 구슬을 구멍을 통해 빼낼 수 있는지를 계산해야한다.

    [설계]
    1. 빨간구슬과 파란구슬에 대해 상,하,좌,우 4방향 BFS를 적용한다.
    2. 예를들어 맵을 오른쪽으로 기울였으면, 빨간구슬과 파란구슬 둘 다 오른쪽 방향 BFS를 적용한다.
    3. 큐에다가 빨간구슬 x, y 좌표와 파란구슬 x, y 좌표 그리고 기울인 횟수 d를 넣어준다.
    3. 기울이고 나서 구슬의 위치는 4차원 배열로 방문처리해둔다. bool visited[rx][ry][bx][by];
    4. 구슬은 벽(#)을 만날때까지 굴러가야한다. 구슬에 대해 BFS를 적용할 때 이 부분을 처리해야한다.
    5. 구슬이 서로 붙어 있는 경우를 고려해야한다. 더 멀리 이동한 구슬을 움직인 방향을 기준으로 한 칸 뒤로 배치시킨다.
    6. 실패하면, -1을 출력 해야한다.

     

    [회고]
    1. BFS를 사용하는 이유?
       -> 최소한의 기울임으로 빨간 구슬을 구멍에 빠뜨려야한다. 
       -> BFS는 가중치가 없는 그래프에서 최단경로를 찾을 때 사용되므로 위 문제에 적용될 수 있다.

     

    2. 빨간 구슬과 파란구슬이 붙어있는 경우를 고려해야함

       -> 처음에 생각지 못해서 어려웠다.

     

    3. 4차원 배열을 통해 방문 처리

       -> 처음에 빨간구슬 2차원 방문 배열, 파란구슬 2차원 방문 배열 이렇게 나눠서함

       -> 빨간 구슬과 파란 구슬이 동시에 움직이기 때문에 4차원 배열을 사용해서 한 번에 방문 처리해야함.

     

    [코드]

    #include<iostream>
    #include<queue>
    using namespace std;
    
    typedef struct {
    	int rx, ry, bx, by, d;
    }pos;
    
    char map[10][10];
    int n, m;
    int res = 11;
    bool visited[10][10][10][10];
    int dx[] = { -1,1,0,0 }, dy[] = { 0,0,-1,1 };
    queue<pos> q;
    
    void move(int &x, int &y, int &Cnt, int dx, int dy) {
    	while (map[x+dx][y+dy] != '#' && map[x][y] != 'O'){
    		x += dx;
    		y += dy;
    		Cnt++;
    	}
    }
    
    void bfs() {
    	while (!q.empty()) {
    		int rx = q.front().rx, ry = q.front().ry;
    		int bx = q.front().bx, by = q.front().by;
    		int d = q.front().d;
    		q.pop();
    
    		if (d >= 10) continue;//기울인 횟수가 10이상이라면, 스킵한다. 
    
    		for (int i = 0; i < 4; i++) {
    			int nrx = rx, nry = ry; //빨강 구슬의 새로운 좌표
    			int nbx = bx, nby = by; //파란 구슬의 새로운 좌표
    			int rCnt = 0, bCnt = 0; //빨강, 파랑 구슬이 굴러간 횟수를 카운트 하는 변수
    			
    			move(nrx, nry, rCnt, dx[i], dy[i]); //빨간구슬 이동
    			move(nbx, nby, bCnt, dx[i], dy[i]); //파란구슬 이동
    
    			if (map[nbx][nby] == 'O') //파란 구슬이 구멍에 빠지면, 실패
    				continue;
    
    			if (map[nrx][nry] == 'O') {//빨간 구슬만 구멍에 빠진 경우라면, 성공
    				res = min(res, d+1);//성공 했을 때, 최솟값 기록
    				continue;
    			}
    
    			if (nrx == nbx  && nry==nby) {//구슬이 겹치는 경우 = 구슬이 붙어있는 경우
    				if (bCnt > rCnt) { //파란구슬이 더 멀리 갔다면, 한 칸 뒤로
    					nbx -= dx[i];
    					nby -= dy[i];
    				}
    				else { //빨간구슬이 더 멀리 갔다면, 한 칸 뒤로
    					nrx -= dx[i];
    					nry -= dy[i];
    				}
    			}
    
    			if (visited[nrx][nry][nbx][nby]) continue; //이미 방문했다면(이미 알아본 경우의 수라면), 스킵
    
    			visited[nrx][nry][nbx][nby] = true; //방문처리
    			q.push({ nrx,nry,nbx,nby,d+1}); //다시 큐에 넣는다. 
    		}
    	}
    }
    
    int main() {
    	cin >> n >> m;
    	int rx, ry, bx, by; //빨간구슬과 파란구슬의 초기위치 값을 저장하는 변수
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < m; j++) {
    			cin >> map[i][j];
    			if (map[i][j] == 'R') {
    				rx = i, ry = j;
    			}
    			else if (map[i][j] == 'B') {
    				bx = i, by = j;
    			}
    		}
    	}
    	visited[rx][ry][bx][by]=true;
    	q.push({ rx,ry,bx,by,0 }); //두 구슬의 초기위치, 기울인횟수(0)을 큐에 넣어준다.
    	bfs();
    	if(res == 11)
    		printf("-1");
    	else 
    		printf("%d", res);
    	return 0;
    }

     

    [출처]

    https://rebas.kr/725

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

    [백준] 1038 감소하는 수  (0) 2020.04.15
    [백준] 16234 인구이동  (0) 2020.03.26
    [백준] 17141 연구소 2  (0) 2020.03.26
    [백준] 16933 벽 부수고 이동하기 3  (0) 2020.03.24
    [백준] 16197 두 동전  (0) 2020.03.08

    댓글

Designed by Tistory.