ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 2206(벽 부수고 이동하기)
    전공/알고리즘 2020. 6. 25. 00:43

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

     

    2206번: 벽 부수고 이동하기

    N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로��

    www.acmicpc.net

     

     

    N, M 이 마무리이고, 벽은 한번만 뚫을 수 있으며, 최단 거리로 간다. 

     

    모든 가능한 경우를 확인해서, 그 중 가장 짧은 거리를 출력한다. 

     

    위 조건들을 보고 이 문제는 무조건 dfs다 라고 생각했다. 하지만 시간초과의 문제에 봉착했고, bfs로 돌렸다. 

    dfs의 시간초과 여부를 계산하는게 아직 많이 어렵다. 아 근데 dfs로 풀린 코드도 있다. 이게 어떻게 가능했는지는 후에 서술하겠다. 

     

    이 문제와 다른 bfs문제들의 차이점은 벽이 존재하는데 한번은 뚫을 수 있다는 것이었다. 

     

    따라서 핵심은 벽을 한번만 뚫는 것을 어떻게 처리할지이다.

     

    우선 bfs는 산탄총같이 한번에 사방으로 나가는 것이라서 각각의 독립적인 노선을 갖고 있다. 

    따라서 만약 벽 변수의 선언을 함수 전체에 하게 된다면 각각 독립적인 노선이지만 벽 부수기 1회 사용을 했다 안 했다로 서로 영향을 주므로 그렇게 선언하면 안된다는 사실을 알 수 있다. 

     

    그렇다면, 어떻게 해야할까. 

     

    경로에 벽이 뚫린건지 아닌건지 알아야 한다. 내가 지나온 경로에서 벽 뚫기 찬스를 사용했다면, 그 노선에서는 쓸 수 없게끔 만들어야한다. 따라서 이전 경로에서 쓰였는지 안 쓰였는지까지 queue에 pair<pair<x좌표, y좌표> pair<경로횟수, 벽사용유무>> 로 저장하였다. 

     

    그리고 가장 중요한 bfs의 특징은 최단거리 먼저 조사한다는 것이다. 그래서 어떤 지점을 최단거리로 밟았을 때, 그 지점을 밟은 경우는 벽을 뚫고 밟은 경우, 아닌 경우 두 가지로만 나뉜다. 

     

    따라서 bool visited[x][y][wall]을 이용해서 사용하였다. 

     

    아 이 과정에서 논리의 문제점이라고 생각한 경우를 보면,

     

    A->B까지 가는데 C지점을 밟고 B로 간 경우(1)와 D지점을 밟고 간 경우(2) 가 같은 거리였을때,

    (1)을 먼저 조사했는데, visited는 true로 변하고, D에서 C를 밟으면서 가는경로가 더 짧을 때 어떻게 될까를 생각했었다. 

     

    하지만 결론은 논리에 문제가 없다는 것이다. 만약 C를 밟으면서 가는 경로가 더 짧다면, (1)에서 그냥 C에서 끝까지 가면 (2)보다는 확실하게 빨리 도착한다. 

     

     

    bool done은 한 경로라도 도달하면, true로 하여서 원하는 결과를 출력하고 아닌 경우에는 -1을 출력하게끔 설정하였다. 

    #include<iostream>
    #include<string>
    #include<queue>
    #include<utility>
    using namespace std;
    #define MAX 1001
    
    int N, M;
    int mat[MAX][MAX];
    int dx[4] = { 1,-1,0,0 };
    int dy[4] = { 0,0,1,-1 };
    queue<pair<pair<int,int>, pair<int, bool> > > q;
    pair<int, int> p;
    bool done = false;
    bool visited[MAX][MAX][2] = { false };
    
    void bfs() {
    	bool wall = false;
    	int cnt;
    	while (!q.empty()) {
    		p.first = q.front().first.first;
    		p.second = q.front().first.second;
    		wall = q.front().second.second;
    		cnt = q.front().second.first;
    		q.pop();
    		if (p.first == N && p.second == M) {
    			cout << cnt << "\n";
    			done = true;
    			return;
    		}
    		for (int i = 0; i < 4; i++) {
    			int x = p.first + dx[i];
    			int y = p.second + dy[i];
    			if (x<1 || y<1 || x > N || y > M) continue;
    			if (!visited[x][y][wall]) {
    				if (!wall && mat[x][y]) {
    					visited[x][y][wall] = true;
    					q.push(make_pair(make_pair(x, y), make_pair(cnt+1, true)));
    				}
    				else if (!mat[x][y]) {
    					visited[x][y][wall] = true;
    					q.push(make_pair(make_pair(x, y), make_pair(cnt + 1, wall)));
    				}
    
    			}
    		}
    	}
    }
    
    
    int main() {
    	ios_base::sync_with_stdio(false);
    	cin.tie(0);
    	cin >> N >> M;
    	string str;
    	for (int i = 1; i <= N; i++) {
    		cin >> str;
    		for (int j = 1; j <= M; j++) {
    			mat[i][j] = str.at(j - 1) - '0';
    		}
    	}
    	q.push(make_pair(make_pair(1, 1), make_pair(1, false)));
    	bfs();
    	if (!done) cout << -1 << "\n";
    }

     

    '전공 > 알고리즘' 카테고리의 다른 글

    백준 2211(네트워크 복구)  (0) 2020.06.25
    백준 1644(소수의 연속합)  (0) 2020.06.25
    백준 9019(DSLR)  (0) 2020.06.22
    백준 2636(치즈)  (0) 2020.06.19
    백준 1697(숨바꼭질)  (0) 2020.06.17

    댓글

Designed by Tistory.