알고리즘 (with JAVA)/동적 계획법

외발 뛰기 (난이도: 하)

백_곰 2022. 9. 27. 10:58

1. 문제 설명

(1) 아래의 그림처럼 NxN 크기의 격자에 1부터 9까지의 정수를 쓴 게임판이 있다.

(2) 이 게임의 목적은 게임판의 왼쪽 위칸에서 시작해서 게임판의 맨 오른쪽 아래칸 "끝" 쪽으로 도착하는 것이다.

 

(3) 이때 각 칸에 적혀 있는 숫자만큼 아래쪽이나 오른쪽으로 이동할 수 있으며, 중간에 게임판 밖으로 벗어나면 안 된다.

 

(4) 문제는 게임판이 주어질 때 시작점에서 끝점까지 도달하는 방법이 존재하는 지를 확인하는 것이다.

 

(5) 예를 들어, 아래의 그림처럼 위의 그림에서 마지막 2를 3으로 바꾸면 방법이 없다.

 

 

 

 

2. 입출력 조건 및 예제

입력 조건

(1) 입력의 첫 줄에는 테스트 케이스의 수 C(1<=C<=50)

(2) 그 후 두 번째 줄에는 N(1<=N<=10)의 크기

(3) 그 후 각  줄에는 board(1<=board<=100)를 초기화한다.

 

출력 조건

(1) True는 1로, False는 0으로 출력한다.

 

입력 예제

2
7
2 5 1 6 1 4 1
6 1 1 2 2 9 3
7 2 3 2 1 3 1
1 1 3 1 7 1 2
4 1 2 3 4 1 2
3 3 1 2 3 4 1
1 5 2 9 4 7 0
7
2 5 1 6 1 4 1
6 1 1 2 2 9 3
7 2 3 2 1 3 1
1 1 3 1 7 1 2
4 1 2 3 4 1 3
3 3 1 2 3 4 1
1 5 2 9 4 7 0

 

출력 예제

1

0

 

 

 

 

3. 제약 조건

X

 

 

 

 

4. 가정법

X

 

 

 

 

5. 기저 사례

(1) board판의 범위를 벗어나면

 

 

 

 

6. 코드

: 동적 계획법으로 바꾸기 위해서 먼저 (1)에서는 완전 탐색을 보여주고 (2)에서는 (1)에서의 완전 탐색 중복 계산을

없애는 메모이제이션을 수행하는 것을 보여준다.

 

 

 

(1) 완전 탐색

public class JUMPGAME1 {
    private static BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
    private static StringBuilder sb = new StringBuilder();

    private static int[][] board ;
    private static int N;

    public static void main(String[] args) throws IOException {
        StringTokenizer st;
        int C = Integer.parseInt(sc.readLine());

        for(int i=0; i<C; i++) {
            N = Integer.parseInt(sc.readLine());
            board = new int[N][N];

            for(int y=0; y<N; y++) {
                st = new StringTokenizer(sc.readLine());
                for(int x=0; x<N; x++) {
                    board[y][x] = Integer.parseInt(st.nextToken());
                }
            }
            sb.append(solve(0,0)+"\n");
        }
        System.out.println(sb);
    }

    private static boolean solve(int y, int x) {
        // 기저 사례(1): 범위를 벗어나면 false
        if(y>=N || x>=N) return false;

        // "끝" 지점에 도달한 경우 성공
        if(y==N-1 && x==N-1) return true;

        // jump 크기를 가지고 완전탐색 시작.
        int jumpSize = board[y][x];
        return solve(y+jumpSize, x) || solve(y, x+jumpSize);
    }
}

 

 

 

(2) 메모이제이션

public class JUMPGAME2 {
    private static BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
    private static StringBuilder sb = new StringBuilder();

    private static int[][] board;
    private static int N;
    private static int[][] cache;
    public static void main(String[] args) throws IOException {
        StringTokenizer st;
        int C = Integer.parseInt(sc.readLine());

        for(int i=0; i<C; i++) {
            N = Integer.parseInt(sc.readLine());
            cache = new int[N][N];
            board = new int[N][N];

            // 캐시 배열 모두 -1로 초기화
            for(int y=0; y<N; y++) {
                for(int x=0; x<N; x++) {
                    cache[y][x] = -1;
                }
            }

            for(int y=0; y<N; y++) {
                st = new StringTokenizer(sc.readLine());
                for(int x=0; x<N; x++) {
                    board[y][x] = Integer.parseInt(st.nextToken());
                }
            }
            sb.append(solve(0,0)+"\n");
        }
        System.out.println(sb);
    }
    private static int solve(int y, int x) {
        // 기저 사례(1): 범위를 벗어나면 false
        if(y>=N || x>=N) return 0;

        // "끝" 지점에 도달한 경우 성공
        if(y==N-1 && x==N-1) return 1;

        // 메모이제이션
        if(cache[y][x] != -1) return cache[y][x];

        // jump 크기를 가지고 완전탐색 시작.
        int jumpSize = board[y][x];
        return cache[y][x] = (solve(y+jumpSize, x) | solve(y, x+jumpSize));
    }
}