Thief of Wealth
시간 제한메모리 제한제출정답맞은 사람정답 비율
2 초192 MB170233713231922.995%

문제

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.

만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.

맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

출력

첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.



문제를 딱 보자마자 생각한 아이디어는, 벽이 있는 위치를 queue에 저장하고

그 위치를 하나하나 없애가면서 모든 경우를 bfs해보는 방법을 생각해냈다.


그러나 느낌적으로 이건 시간초과다 싶었고,

별다른 아이디어가 떠오르지 않아서  일반 bfs코드만 짜놓고, 풀이를 검색했다.


풀이를 검색해서 아이디어를 얻었다.

상하좌우를 체크할때 이전에 벽을 부쉈는지 여부를 체크하여 que에 넣는 것이다.

물론 struct에 과거에 부쉈는지 여부를 체크하는 변수를 만들어줘야한다.


부순적이 없으면 1일때 부수고 0이면 그냥 지나간다.

부쉈으면 que에는 부쉈다고 기록해서 저장한다.


부순적이 있으면 1이면 못가고 0이면 그냥간다.


====

여기 까지하고 유레카!하면서 예제통과하고 제출했더니 틀렸습니다가 칼답으로 왔다.


2차멘붕이 와서 풀이는 되도록 안보려하다가

8 8
01000100
01010100
01010100
01010100
01010100
01010100
01010100
00010100


라는 반례를 보게되고, 왜 29가 나오는지 생각해보았다.


기존 bfs알고리즘에 que넣을때 crash체크하는것만 추가했다면 위 반례에서 -1이 나올것이다.

왜냐하면 좌측상단에서 우측하단으로 탐색하면서 모든곳을 visited 하면서 탐색하기 때문에

밑에서 다시 올라오는 경로는 더이상 탐색할 수 없게된다.


그래서 또 visited관련한 아이디어를 못찾은 나는,

풀이를 2차로 봤다.


visited를 3차원으로 만든다 2 x 1001 x 1001

그리고 que넣는 부분 체크하는 곳에 현재위치의 crashed여부를 판단하여 

부수고 방문하고 있는지, 부수지 않고 방문하고 있는지를 나눈다.

이 부분은 좀더 자세하게 이해해볼 필요가있다.

처음에 2가지 방향으로 출발해서 경로가 아니라 값을 찾는 "탐색" 한다는 개념으로 이해하자.


#include <bits/stdc++.h>
using namespace std;

int x_direction[4] ={-1,1,0,0};
int y_direction[4] ={0,0,-1,1};

struct Point{
int x;
int y;
bool crashedBefore;
};

bool wall[1001][1001]; // true = not route
bool visited[2][1001][1001]; // false = not visited
int n,m;
queue<Point> que;

int bfs();

int main(){

freopen("input.txt","r",stdin);

ios::sync_with_stdio(false);
cin.tie(0);


cin>>n>>m;

memset(wall,false, sizeof(wall));
memset(visited,false, sizeof(visited));
string temp;
for(int i=0; i<n; ++i){
cin>>temp;
for(int k=0; k<temp.length(); ++k){
if( temp.at(k) == '1' ){ // 1 is not route
wall[i][k] = true;
}
}
}

que.push({0,0,false});
visited[0][0][0]=true;
cout<<bfs();

return 0;
}

int bfs(){
int total_step=0;
Point p = que.front();
int step_width = que.size();
bool beforeCrash=p.crashedBefore;
while(!que.empty()){

step_width = que.size();
total_step ++;

for(int s=0; s<step_width; ++s){
p = que.front();
if(p.x == n-1 && p.y == m-1){ //found?
return total_step;
}

que.pop();
beforeCrash = p.crashedBefore;
for(int i=0; i<4; ++i){
if(
(p.x+x_direction[i]>=0 && p.x+x_direction[i]<n) &&
(p.y+y_direction[i]>=0 && p.y+y_direction[i]<m) &&
(!visited[p.crashedBefore][p.x+x_direction[i]][p.y+y_direction[i]])
){
if(!beforeCrash){ // crash before? NO!
//You can crash!
if( wall[p.x+x_direction[i]][p.y+y_direction[i]] == 1 ){ //There is wall
//crash!
visited[p.crashedBefore][p.x+x_direction[i]][p.y+y_direction[i]] = true;
que.push({p.x+x_direction[i],p.y+y_direction[i],true});
} else { //There is no wall, keep going
visited[p.crashedBefore][p.x+x_direction[i]][p.y+y_direction[i]] = true;
que.push({p.x+x_direction[i],p.y+y_direction[i],false});
}
} else { //crashed before? Yes!
if(wall[p.x+x_direction[i]][p.y+y_direction[i]] == 0) { // you can pass if there is no wall.
visited[p.crashedBefore][p.x+x_direction[i]][p.y+y_direction[i]] = true;
que.push({p.x+x_direction[i],p.y+y_direction[i],true});
}
}
}
}
}
}
return -1; // didnt find
}



'개발 > 알고리즘' 카테고리의 다른 글

2573 빙산  (0) 2019.04.14
2468 안전영역  (0) 2019.04.14
10026 적록색약  (0) 2019.04.12
7562 나이트의 이동  (0) 2019.04.12
11404 플로이드  (0) 2019.04.11
profile on loading

Loading...