Skip to content

Conversation

@GeumBi-Hong
Copy link

풀이

  • 2차원 배열가지고 문제를 풀어보았습니다.
  • 좀 더 자세한 설명은 주석과 블로그 주소에 남겨 놓았습니다. (https://geumba.tistory.com/55)

이 문제의 핵심은 이러합니다.

1. 2차원 int[][]map에 방문한 좌표값에 ' 말처럼 이동 할 수 있는 남은 횟수'를 저장하여 방문처리를 하는 것입니다.
2. 큐에 들어가는 총 이동 횟수는 (Info.move) 항상 1........2..............3 이렇게 오름차순으로 들어갈 수 밖에없습니다. 따라서 도착지점의 좌표값이라면 그때의 큐에서 꺼낸 이동 횟수가 최소 움직임 (정답값) 일 수 밖에 없습니다.

2번에 대해서 먼저 설명해보겠습니다.

저는 제 풀이에서

static class Info {
        int r;   //행 좌표
        int c;   // 열 좌표
        int hores; //말로 이동할 수 있는 남은 기회
        int move ; //움직인 횟수

        public Info(int r, int c, int hores,int move) {
            this.r=r;
            this.c=c;
            this.hores=hores;
            this.move=move;
        }
    }

로 따로 class를 만들어 bfs 큐에 값을 넣었습니다.

처음 시작 좌표 (0,0)에 있었다면

Info (r= 0, c = 0, hores= K ,move = 0) 일 겁니다.
여기서 원숭이는 4방(상하좌우)로 이동하거나 8방(체스의 나이트 처럼) 으로 총 12방향으로 이동 할 수 있습니다. (8방은 남은 횟수가 존재한다는 가정하에 얘기임)

이 차례에서 큐에 들어가는 값들은 모두 한번의 동작으로 움직인거기때문에 move =1인 상태로 들어가게됩니다.
그럼 또 큐에서 move=1인 Info를꺼내와 4방,8방의 경우를 모두 해준다면 이 차례에서 들어가는 값은 move =2일 겁니다.
여기서 큐에 특성상 앞에 move =1인 경우가 먼처 처음으로 들어와있기때문에 move=1인 값들이 먼저 처리가 되주어야 move=2인 경우를 따지게 됩니다. 따라서 원숭이가 도착지에 도착할 수 있는 경우에 move값이 최소 동작이 될 수 밖에 없습니다.

그렇다면 이제 방문 처리를 해주어야합니다. 방문 처리를 해주지 않는다면 방문한 좌표를 계속 방문하게 되는 무한 루프 상태에 걸리게 될테니까요.

한 번 방문한 좌표에 다시 들어 갈 수 없게끔 처리하면 되지않나? 왜냐면 처음 방문했을때 move 값이 가장 최소일꺼니까 ! 라고 생각하고 처리해버린다면 안됩니다.

아래의 코드를 설명해보면

    //상 하 좌 우 인접이동
            for(int i = 0 ; i <4 ; i++){

               int nr = curR + dr[i];
               int nc = curC + dc[i];

               //2차원 배열의 범위를 넘어가는 경우는 넘겨준다.
                if (!isRange(nr, nc) )continue;

                //남은 말처럼 이동할 수 있는 횟수가 map[nr][nc]보다 큰 경우
                //이 조건을 한 이유는
                // 예를 들어 어떠한 좌표에 원숭이가 도착했다고 치자. (3,2)좌표라고 해보겠다.
                // 이 좌표에 처음 도착했을때 move값이 그 칸에 갈 수 있는 가장 최소의 값일 것이다.
                // 이때 남은 말의 회수가 1이였다고 치고 map[3][2]=1을 저장하였다.
                //또 어떠한 경로로 인해 map[3][2]에 올 수 있는 경우가 있다. 이때 move값은 처음도착했을때의 값보다 클 것이다.
                //하지만 map[nr][nc]=1이였을 때 , 이 걸로 도착지(정답)에 도착한다는 보장이 없다.
                //따라서 map[nr][nc] <curHourse 말로이동할 수 있는더 큰 경우가 존재한다면 그 경우에도 탐색을 해 주어야하기때문에 이 조건문을 썼다.
                if(map[nr][nc]<curHourse){

                    //도착지에 도착했다면 이동 횟수를 리턴한다.
                    //큐에서 꺼낸 Info의 move값은 최소값으로 수 밖에 없다.
                    //큐에 move가 1일때,2일때,3일때 순으로 차례로 저장되기 때문이다.
                    if(nr==R-1 && nc ==C-1){
                        return nextMove;
                    }
                    //map에 남은 말처럼 이동 할 수 있는 횟수를 저장한다.
                    map[nr][nc] = curHourse;
                    queue.add(new Info(nr, nc, curHourse, nextMove));

                }

            }

if(map[nr][nc]<curHourse)

한번 방문한 좌표는 다시 들어오지않게 처리하는 것이 아닌, map[nr][nc] (nr,nc까지 오는데 남은 말 횟수) 가 curHourse ( 어떤 다른 경로로 또 왔을때 남은 말횟수) 보다 작다면 한 번 더 다시 방문을 해주어야합니다.

그 이유는 예를들어 map[3][2]에 = 2(남은 말 횟수) 라는 값으로 처음 값이 찍혔을때 map[3][2]에 = 2 이 경우에서 다음 좌표로 이동하는 경우들이 또 생겨 나게 되는데 이 경우들이 도착지(정답)에 도착한다는 보장이 없기때문입니다. 따라서 nr =3 , nc=2, 일때 curHourse 가 4라고 한다면 전에 저장되어있는 값 2 보다 4가 훨씬 더 많은 경로로 이동하여 탐색 할 수 있기때문에 (map[nr][nc]<curHourse) 라는 조건으로 방문 처리를 해주어야합니다.

이런식으로 탐색하게 되었을때 도착지에 도착한다면 그때까지 이동한 횟수의 값이 가장 최소이기때문에 그 값을 정답으로 해주면 됩니다.

** 생각해볼만한 점**
저는 처음에 Queue를 선언했을때 LinkedList로 선언하였습니다. 그런데 메모가 9만 정도 , 시간은 600ms 정도 나왔습니다.
여기서 Queue를 ArrayDeque로 선언하니 메모리는 5만대 , 시간은 488ms 가 나왔습니다. 메모리 부분에서 엄청 차이가 난다는걸
볼 수 있습니다.

따라서 Queue를 LinkedList로 선언했을때와 ArrayDeque로 선언했을 때 차이점을 한번 알아보신다면 좋을 거같아 링크 몇개 남겨 두겠습니다.

리뷰 요청 사항

  • 제가 적어놓은 풀이가 이해가 안되시거나 설명이 잘못 되어있다면 지적부탁드립니다 :)
  • 설명이 이해가 잘 안가시면 따로 저에게 연락주세용

느낀점

  • 원숭이는 그냥 원숭이처럼 이동하자.

@GeumBi-Hong GeumBi-Hong changed the title Week 6 홍금비 풀이 Week6 홍금비 1600 말이되고픈원숭이 풀이 (2차원 배열만을 가지고 품) Oct 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant