본문 바로가기
카테고리 없음

1890번 - 점프(dp)

by HWK 2024. 8. 7.

문제:

N x N 크기의 바둑판이 있다. 각 칸에는 점프 할 수 있는 칸수가 적혀져있고, 오른쪽 또는 아래로만 이동이 가능하다.

좌측상단에서 우측하단까지 이동해야한다.

첫째 줄에 게임 판의 크기 N (4 ≤ N ≤ 100)이 주어진다. 그 다음 N개 줄에는 각 칸에 적혀져 있는 수가 N개씩 주어진다. 칸에 적혀있는 수는 0보다 크거나 같고, 9보다 작거나 같은 정수이며, 가장 오른쪽 아래 칸에는 항상 0이 주어진다.

가장 왼쪽 위 칸에서 가장 오른쪽 아래 칸으로 문제의 규칙에 맞게 갈 수 있는 경로의 개수를 출력한다. 경로의 개수는 2^63-1보다 작거나 같다.

 

풀이:

경로 개수는 2^63-1보다 작거나 같으므로 꽤 많은 경우의 수가 나올 수 있다.

이럴때 BFS를 이용하면 메모리초과, DFS를 이용하면 시간초과가 나올 것이다.

이 문제는 경우의 수를 구하는 것으로, 각 칸에서 점프를 하며 다음 칸의 경우의 수에 + 1만 해주면 된다.

먼저 아래와 같이 입력을 받아준다.

static int N;
static int[][] jump;
static long[][] dp;

public static void main(String[] args) throws IOException{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    N = Integer.parseInt(br.readLine());

    jump = new int[N][N];
    for (int i = 0; i < N; i++) {
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int j = 0; j < N; j++) {
            jump[i][j] = Integer.parseInt(st.nextToken());
        }
    }
    dp = new long[N][N];
    dp[0][0] = 1;

    routeCount();

    System.out.println(dp[N - 1][N - 1]);
}
  • jump 배열은 각 칸의 점프 칸수를 저장한다.
  • dp는 각 칸을 갈 수 있는 경우의 수를 저장한다.
    고로 시작점인 dp[0][0]에는 1이 저장된다..

routeCount() 메서드는 아래와 같다.

static void routeCount() {
    for (int y = 0; y < N; y++) {
        for (int x = 0; x < N; x++) {
            if (dp[y][x] == 0) {
                continue;
            }
            if (jump[y][x] == 0) {
                break;
            }
            if (y + jump[y][x] < N) {
                dp[y + jump[y][x]][x] += dp[y][x];
            }
            if (x + jump[y][x] < N) {
                dp[y][x + jump[y][x]] += dp[y][x];
            }
        }
    }
}
  1. 만약 dp[y][x]가 0이면 건너뛴다. 자세한 설명은 아래에서 하겠다.
  2. 만약 jump[y][x]가 0이면 목적지에 도달한 것이니 반복을 종료한다.
  3. 아래로 점프 했을 때, 범위에서 벗어나지 않았다면
    1. 점프한 칸에 지금 칸에 도착할 수 있는 경우의 수를 더해준다.
  4. 오른쪽으로 점프 했을 때, 범위에서 벗어나지 않았다면
    1. 점프한 칸에 지금 칸에 도착할 수 있는 경우의 수를 더해준다.
  5. 1~4번 과정을 시작점(0,0)에서 목적지(N-1, N-1)까지 반복한다.

dp[][]에 0이 담겨있다면, 이는 시작점에서 도달할 수 없는 위치라는 의미이다.

해당 위치에 도달하기 전에, 미리 해당 위치에 갈 수 있는 경우의 수가 결정되어 있기 때문이다.

만약 dp에 0이 담겨있는걸 검사하지 않는다면, 모든 칸에대해 검사를 해야 할 것이다.

 

전체코드:

import java.io.*;
import java.util.*;

public class 점프_1890번 {
    static int N;
    static int[][] jump;
    static long[][] dp;

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());

        jump = new int[N][N];
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                jump[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        dp = new long[N][N];
        dp[0][0] = 1;

        routeCount();

        System.out.println(dp[N - 1][N - 1]);
    }

    static void routeCount() {
        for (int y = 0; y < N; y++) {
            for (int x = 0; x < N; x++) {
                if (dp[y][x] == 0) {
                    continue;
                }
                if (jump[y][x] == 0) {
                    break;
                }
                if (y + jump[y][x] < N) {
                    dp[y + jump[y][x]][x] += dp[y][x];
                }
                if (x + jump[y][x] < N) {
                    dp[y][x + jump[y][x]] += dp[y][x];
                }
            }
        }
    }

}

 

 

시행착오:

몇번의 경우가 나오는지 체크도 안하고, BFS를 이용하면 쉽게 풀 수 있을 것이라 생각했다.

하지만 무려 경로 개수는 2^63-1보다 작거나 같으므로 메모리 초과가 일어났다.

더보기
public class 점프_1890번_메모리초과 {
    static int N;
    static int[][] jump;
    static int[][] sum;

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());

        jump = new int[N][N];
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                jump[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        sum = new int[N][N];
        sum[0][0] = 1;

        routeCount();

        System.out.println(sum[N - 1][N - 1]);
    }

    static void routeCount() {
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[]{0, 0});

        while (!queue.isEmpty()) {
            int[] now = queue.poll();
            int nowY = now[0];
            int nowX = now[1];
            if (nowX == N - 1 && nowY == N - 1) {
                continue;
            }
            if (nowY + jump[nowY][nowX] < N) {
                queue.add(new int[]{nowY + jump[nowY][nowX], nowX});
                sum[nowY+ jump[nowY][nowX]][nowX] += sum[nowY][nowX];
            }
            if (nowX + jump[nowY][nowX] < N) {
                queue.add(new int[]{nowY, nowX + jump[nowY][nowX]});
                sum[nowY][nowX + jump[nowY][nowX]] += sum[nowY][nowX];
            }
        }
    }

}

 

혹시나 하고 dfs도 해봤지만, 당연하게도 시간초과가 발생했다.
재귀로 왔다 갔다 하기에는 시간이 너무 오래걸리기 때문이다.

 

이후 dp를 사용해서 문제를 풀었을 때도, 불필요한 코드를 작성했는데,
dp[y][x] == 0이면 들를 필요가 없음애도, visited 배열을 작성했었다.

 

dp가 아직 익숙치 않은 듯 하다. 더더욱 반복해보자.