2151번 거울 설치
문제
채영이는 거울을 들여다보는 것을 참 좋아한다. 그래서 집 곳곳에 거울을 설치해두고 집 안을 돌아다닐 때마다 거울을 보곤 한다.
채영이는 새 해를 맞이하여 이사를 하게 되었는데, 거울을 좋아하는 그녀의 성격 때문에 새 집에도 거울을 매달만한 위치가 여러 곳 있다. 또한 채영이네 새 집에는 문이 두 개 있는데, 채영이는 거울을 잘 설치하여 장난을 치고 싶어졌다. 즉, 한 쪽 문에서 다른 쪽 문을 볼 수 있도록 거울을 설치하고 싶어졌다.
채영이네 집에 대한 정보가 주어졌을 때, 한 쪽 문에서 다른 쪽 문을 볼 수 있도록 하기 위해 설치해야 하는 거울의 최소 개수를 구하는 프로그램을 작성하시오.
거울을 설치할 때에는 45도 기울어진 대각선 방향으로 설치해야 한다. 또한 모든 거울은 양면 거울이기 때문에 양 쪽 모두에서 반사가 일어날 수 있다. 채영이는 거울을 매우 많이 가지고 있어서 거울이 부족한 경우는 없다고 하자.
거울을 어떻게 설치해도 한 쪽 문에서 다른 쪽 문을 볼 수 없는 경우는 주어지지 않는다.
입력
첫째 줄에 집의 크기 N (1 ≤ N ≤ 50)이 주어진다. 다음 N개의 줄에는 N개의 문자로 집에 대한 정보가 주어진다. ‘#’는 문이 설치된 곳으로 항상 두 곳이며, ‘.’은 아무 것도 없는 것으로 빛은 이 곳을 통과한다. ‘!’은 거울을 설치할 수 있는 위치를 나타내고, ‘*’은 빛이 통과할 수 없는 벽을 나타낸다.
출력
첫째 줄에 설치해야 할 거울의 최소 개수를 출력한다.
예제 입력
5
***#*
*.!.*
*!.!*
*.!.*
*#***
8
***#****
*!.!..!*
*......*
*..*...*
*!!....*
*.!!..!*
*......*
***#****
5
***#!
*.*..
*!.*.
*!..!
*#***
8
****!.#!
*...!..!
*.......
#......!
*......*
*......*
*......*
********
3
...
*!#
#!!
예제 출력
2
4
3
2
1
해결방법
참고블로그1 : https://lyzqm.blogspot.com/2017/04/2151.html
참고블로그2 : http://wondy1128.tistory.com/171
4시간이 걸린 문제이다... 흑흑
BFS 탐색으로 문제를 풀다가 블로그를 참고하여 결국 다익스트라로 문제를 해결했다
⭐️ 1차 시도 ⭐️
틀렸습니다
단순하게 BFS 탐색으로 접근했다
첫 번째 문의 방향을 구한다음 큐에 넣고 최단거리 탐색을 수행했다
하지만 문제는 최단거리가 아닌 최소한의 방법을 사용하는 탐색이었다
최소 방법 탐색은 우선순위 큐
, 다익스트라 알고리즘
을 사용하여 해결해야 했다
⭐️ 2차 시도 ⭐️
틀렸습니다
우선순위 큐를 사용하여 문제를 접근했다
질문 검색을 하다가 간과했던 사실이 있었다
- 문의 방향은 하나가 아닌 여러개일 수 있다
- 최소의 거울을 설치하여 이동해야 한다
- 거울을 설치하여 방향을 바꿀수도, 설치하지 않고 그대로 진행할 수도 있다
따라서 처음 문의 방향을 큐에 넣을 때, 맵을 벗어나지 않고 벽이 아닌 모든 방향을 넣어줬다
// 방향 세팅
void setDoorDirection(){
int x = door[0].x;
int y = door[0].y;
for(int d=0; d<4; d++){
int nx = x + dx[d];
int ny = y + dy[d];
if(nx<0 || ny<0 || nx>=n || ny>=n) continue;
if(map[nx][ny] == '*') continue;
pq.push(node(x,y,d,0));
visited[x][y][d] = true;
}
}
또한 다음 노드의 상태에 따라 진행여부를 달리해줬다
먼저 거울인 경우, 방향에 따라 회전을 시켜서 탐색을 진행했다
if(map[nx][ny] == '!'){
if(d==0 || d==1){
for(int i=2; i<=3; i++){
pq.push(node(nx,ny,i,cnt+1));
visited[nx][ny][i] = true;
}
}
if(d==2 || d==3){
for(int i=0; i<=1; i++){
pq.push(node(nx,ny,i,cnt+1));
visited[nx][ny][i] = true;
}
}
}
그리고 나머지 경우 (빈 칸, 문, 거울 설치X) 에 대해서 탐색을 진행해줬다
// 나머지 경우
pq.push(node(nx,ny,d,cnt));
visited[nx][ny][d] = true;
하지만 어떤 예외가 있는지 결과가 나오지 않았다 😭😭
⭐️ 3차 시도 ⭐️
맞았습니다
테스트케이스를 발견했다!!
// INPUT
3
...
*!#
#!!
// OUTPUT
>> 1
이 TC 에서는 답이 2로 나왔다
디버깅을 해보니 문(1,2)
에서 왼쪽(1,1)
아래쪽(2,2)
으로 거울을 설치한 다음의 경우에서 문제가 발생했다
(1,1) 에서 (2,1) 로 이동 후, visited 을 true 로 설정하게 되면 (2,2) 에서는 거울을 설치하고 갈 수 있지만 이미 방문했기 때문에 더이상 진행되지 못한다
나는 우선순위 큐만 사용하면 된다고 생각했는데 다익스트라 알고리즘의 Relax연산
을 같이 수행해야했다
bool형의 visited 배열이 아닌 int형의 dist 배열로 변경한 후, Relax 연산을 적용하니 결과가 잘나왔다!
다음 노드가 거울인 경우
if(map[nx][ny] == '!'){
if(d==0 || d==1){
for(int i=2; i<=3; i++){
if(dist[nx][ny][i] > dist[x][y][d] + 1){
pq.push(node(nx,ny,i,cnt+1));
dist[nx][ny][i] = dist[x][y][d] + 1;
}
}
}
if(d==2 || d==3){
for(int i=0; i<=1; i++){
if(dist[nx][ny][i] > dist[x][y][d] + 1){
pq.push(node(nx,ny,i,cnt+1));
dist[nx][ny][i] = dist[x][y][d] + 1;
}
}
}
}
나머지 경우 (빈 칸, 문, 거울 설치X)
// 나머지 경우
if(dist[nx][ny][d] > dist[x][y][d]){
pq.push(node(nx,ny,d,cnt));
dist[nx][ny][d] = dist[x][y][d];
}
소스코드
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#define MAX 51
#define INF 987654321
using namespace std;
struct node {
int x,y,d,cnt;
node() { }
node(int _x,int _y,int _d,int _cnt) : x(_x),y(_y),d(_d),cnt(_cnt) { }
bool operator > (const node &n) const{
return cnt > n.cnt;
}
};
int n;
char map[MAX][MAX];
int dist[MAX][MAX][4];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
priority_queue<node, vector<node>, greater<node>> pq;
vector<node> door;
// 방향 세팅
void setDoorDirection(){
int x = door[0].x;
int y = door[0].y;
for(int d=0; d<4; d++){
int nx = x + dx[d];
int ny = y + dy[d];
if(nx<0 || ny<0 || nx>=n || ny>=n) continue;
if(map[nx][ny] == '*') continue;
pq.push(node(x,y,d,0));
dist[x][y][d] = 0;
}
}
void dijkstra(){
while(!pq.empty()){
int x = pq.top().x;
int y = pq.top().y;
int d = pq.top().d;
int cnt = pq.top().cnt;
pq.pop();
if(x==door[1].x && y==door[1].y){
cout << cnt << "\n";
return;
}
int nx = x + dx[d];
int ny = y + dy[d];
if(nx<0 || ny<0 || nx>=n || ny>=n) continue;
if(map[nx][ny] == '*') continue;
if(map[nx][ny] == '!'){
if(d==0 || d==1){
for(int i=2; i<=3; i++){
if(dist[nx][ny][i] > dist[x][y][d] + 1){
pq.push(node(nx,ny,i,cnt+1));
dist[nx][ny][i] = dist[x][y][d] + 1;
}
}
}
if(d==2 || d==3){
for(int i=0; i<=1; i++){
if(dist[nx][ny][i] > dist[x][y][d] + 1){
pq.push(node(nx,ny,i,cnt+1));
dist[nx][ny][i] = dist[x][y][d] + 1;
}
}
}
}
// 나머지 경우
if(dist[nx][ny][d] > dist[x][y][d]){
pq.push(node(nx,ny,d,cnt));
dist[nx][ny][d] = dist[x][y][d];
}
}
}
int main(int argc, const char * argv[]) {
// cin,cout 속도향상
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
cin >> map[i][j];
if(map[i][j] == '#') door.push_back(node(i,j,-1,0));
for(int k=0; k<4; k++)
dist[i][j][k] = INF;
}
}
setDoorDirection();
dijkstra();
return 0;
}
'Algorithm > BOJ 문제풀이' 카테고리의 다른 글
[DFS] 2661번 좋은수열 (0) | 2019.01.07 |
---|---|
[BFS] 2636번 치즈 (0) | 2019.01.06 |
[BF] 2503번 숫자 야구 (0) | 2019.01.05 |
[BF] 1018번 체스판 다시 칠하기 (0) | 2019.01.05 |
[BF] 10448번 유레카 이론 (0) | 2019.01.04 |