Post

[leetcode / medium] 3286. Find a Safe Walk Through a Grid - (bfs + dp) (Java)

[리트코드] 3286. Find a Safe Walk Through a Grid 문제를 Java를 사용해 bfs + dp로 풀었습니다.

[leetcode / medium] 3286. Find a Safe Walk Through a Grid

🙋‍♂️ 들어가며

문제에서 m, n <= 50 인데 나는 당연히 2차원 상태관리 DFS가 가능할 것이라고 생각했으나 결국 시간초과가 나서 실패하였다

그 이유로는 당연히 $4^{50 \times 50}$ 이기 때문이다

여기서 왜 4가 나오냐면, 동서남북 방향을 탐색하니 4다

틀린 코드 이후 정답코드에 대한 접근도 설명하겠다

❌ 틀린 코드 (2차원 DFS 상태관리)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class Solution {
    static int row, col;
    static boolean answer;
    static int sr, sc, er, ec;
    static int[][] arr;

    static int[] dr = {-1,1,0,0};
    static int[] dc = {0,0,-1,1};

    static boolean can_go_or_not;


    public boolean findSafeWalk(List<List<Integer>> grid, int health) {
        
        // 1. 행, 열
        row = grid.size();
        col = grid.get(0).size();
        arr = new int[row][col];

        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                arr[r][c] = grid.get(r).get(c);
            }
        }


        // 2. 값 설정
        sr = 0;
        sc = 0;
        er = row-1;
        ec = col-1;

        // 3. dfs (방문배열 상태관리) 시작
        can_go_or_not = false;
        boolean[][] visited = new boolean[row][col];
        visited[sr][sc] = true;
        if (arr[sr][sc] == 1) {
            health--;
        }
        dfs(sr, sc, visited, health);
        return can_go_or_not;
    }



    // 4. dfs 함수 (상태관리)
    static void dfs(int cr, int cc, boolean[][] visited, int health) {
        // 4-0. 만약 도달했다면 진행하던 모든 탐색 종료
        if (can_go_or_not) return;
        
        // 4-1. 끝에 도착했는데 체력도 1이상이면 true하고 종료
        if (cr == er && cc == ec && health >= 1) {
            can_go_or_not = true;
            return;
        }

        // 4-2. 체력 0이면 죽은거니까 그냥 종료
        if (health == 0) return;

        // 4-3. 이동가능한지 확인
        for (int d = 0; d < 4; d++) {
            int nr = cr + dr[d];
            int nc = cc + dc[d];

            // 4-3-a. 격자범위 벗어나면 skip
            if (nr < 0 || nr >= row || nc < 0 || nc >= col) continue;

            // 4-3-b. 격자 범위안이지만, 방문했다면 skip
            if (visited[nr][nc]) continue;


            // 4-3-c. 격자 범위 안이고, 방문 안했을 때
            // 4-3-d. 다음 좌표가 0이라면?
            if (arr[nr][nc] == 0) {
                // 4-3-e. 방문처리후, dfs
                visited[nr][nc] = true;
                dfs(nr, nc, visited, health);
                // 4-3-f. 방문해제
                visited[nr][nc] = false;
            }
            // 4-3-g. 그게 아니라 다음 좌표가 1이라면?
            else if (arr[nr][nc] == 1) {
                // 4-3-h. 위와 동일하게 처리하지만, 다만 체력은 1을 빼주자
                visited[nr][nc] = true;
                dfs(nr, nc, visited, health-1);
                visited[nr][nc] = false;
            }
        }


    }



}




고민 🤔

이후 2차원 DP를 활용해 -1로 초기화하고 최소값 찾을때처럼, 목숨의 수가 최대일때를 유지할 수 있도록 갱신하는 코드를 작성하였다.

시간복잡도는 아래와 같다

O(N * M)

N, M <= 50 이라 충분

✅ 정답 코드 (2차원 DP + bfs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import java.util.Queue;
import java.util.LinkedList;


class Solution {
    static int row, col;
    static int[][] DP;
    static int sr, sc, er, ec;

    static int[] dr = {-1,1,0,0};
    static int[] dc = {0,0,-1,1};

    public boolean findSafeWalk(List<List<Integer>> grid, int health) {
        
        // 1. 행, 열
        row = grid.size();
        col = grid.get(0).size();
        DP = new int[row][col];
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                DP[r][c] = -1;
            }
        }

        // 2. 위치 설정
        sr = 0;
        sc = 0;
        er = row-1;
        ec = col-1;
        
        // 3=1. bfs 생성, 첫 원소넣고 방문 시작
        int start_health = health;
        Queue<int[]> q = new LinkedList<>();

        // 3-2. 만약 첫 시작점이 1이라면,체력 1 깎고 시작
        if (grid.get(0).get(0) == 1) {
            start_health--;
            DP[0][0] = start_health;
        }
        // 3-3. 조기종료
        if (start_health == 0) return false;

        // 3-4. 그 외는 탐색 시작
        q.add(new int[] {sr, sc, start_health});
        
        // 4. 검사
        boolean is_alive = false;
        while (!q.isEmpty()) {
            int[] cur = q.poll();
            int cr = cur[0];
            int cc = cur[1];
            int cur_health = cur[2];

            // 4-1. 끝점 도달했을때
            if (cr == er && cc == ec) {
                // 4-1-a. 현재 체력이 1이상이면
                if (cur_health >= 1) is_alive = true;

                // 4-1-b. 조기종료
                if (is_alive) break;
            }

            // 4-2. 아직 도달 못했으면
            for (int d = 0; d < 4; d++) {
                int nr = cr + dr[d];
                int nc = cc + dc[d];

                // 4-3. 격자 범위 밖이면 skip
                if (nr < 0 || nr >= row || nc < 0 || nc >= col) continue;

                // 4-4. 격자범위에 속해 있을 때
                int cur_status = grid.get(nr).get(nc);

                // 4-6. 다음 체력
                int next_health = cur_health - cur_status;

                // 4-7. 만약 다음체력이 1이상, DP보다 크다면?
                if (next_health >= 1 && next_health > DP[nr][nc]) {
                    DP[nr][nc] = next_health;
                    q.add(new int[] {nr, nc, next_health});
                }

            }

        }


        return is_alive;
    }
}
This post is licensed under CC BY 4.0 by the author.

Trending Tags