전공/알고리즘

백준 2665(미로만들기)

xkdlaldfjtnl 2020. 6. 28. 16:40

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

 

2665번: 미로만들기

첫 줄에는 한 줄에 들어가는 방의 수 n(1≤n≤50)이 주어지고, 다음 n개의 줄의 각 줄마다 0과 1이 이루어진 길이가 n인 수열이 주어진다. 0은 검은 방, 1은 흰 방을 나타낸다.

www.acmicpc.net

N^2의 격자에 흰색과 검은색이 존재한다. 검은색은 벽으로 이루어져서 지나갈 수 없다.

 

하지만 벽을 부술 수 있다. 최소 몇번의 벽을 부셔야 시작점에서 종료지점까지 갈 수 있는가

 

어디 지점에서 어디 까지 가는 방법 => dfs, bfs 

 

 

사실 먼저 dfs를 생각했다. dfs의 재귀함수로 몇 번의 벽을 부술지 기록하기가 더 쉽다고 생각했지만, 역시 문제점은 시간 만약에 dfs로 구현하게 된다면 벽이 보이는대로 다 부술테고 최소의 벽 부수기를 원하는 문제에는 알맞지 않다고 생각을 하였다. 

 

따라서 bfs와 priority_queue를 이용해서 벽을 더 적게 부순 경우를 먼저 조사하였다. 

 

bfs의 작동방법을 서술하면 

일단 네 방향으로 퍼진다. 

네 방향 중에 이동할 수 없는 경우인 경우 skip(index범위 초과, 이미 방문한 곳(벽 부순 횟수까지))

그 퍼진 곳 중에 벽이면 벽을 뚫고 priority_queue에 넣는다. 값에 벽 뚫은 횟수 +1

벽이 아닌 경우에 그대로 priority_queue에 넣는다.

queue가 빌때까지 반복

 

그리고 만약에 도착점에 도달했으면 벽 뚫은 횟수가 무조건 최솟값이므로 그 값을 출력.

 

#include<iostream>
#include<queue>
#include<utility>
using namespace std;
#define MAX 50

priority_queue<pair<int , pair<int, int> > > pq;
pair<int, pair<int,int> > p;
int N, answer;
int room[MAX][MAX];
int dx[4] = { 1,-1,0,0 };
int dy[4] = { 0,0,1,-1 };
bool visited[MAX][MAX][MAX] = { false };

void bfs() {
	while (!pq.empty()) {
		p = pq.top();
		pq.pop();
		if (p.second.first == N - 1 && p.second.second == N - 1) {
			answer = -p.first;
			return;
		}
		for (int i = 0; i < 4; i++) {
			int x = p.second.first + dx[i];
			int y = p.second.second + dy[i];
			if (x < 0 || y < 0 || x >= N || y >= N) continue;
			if (visited[-p.first][x][y]) continue;
			if (room[x][y] == 0) {
				pq.push(make_pair(p.first - 1, make_pair(x, y)));
				visited[-p.first + 1][x][y] = true;
			}
			else {
				pq.push(make_pair(p.first, make_pair(x, y)));
				visited[-p.first][x][y] = true;
			}
		}
	}
}

int main() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			char a;
			cin >> a;
			room[i][j] = a - '0';
		}
	}

	pq.push(make_pair(0, make_pair(0, 0)));
	bfs();
	cout << answer << "\n";
}