https://www.acmicpc.net/problem/1890
코드설명
dp 문제입니다.
처음에 기본적으로 dp를 해당 위치까지 올 수 있는 경우의 수로 생각하여
각 위치에서 이동거리에 위치한 점에 도달할때마다 아래의 코드와 같이 갱신하도록 설정하는 것까지는 구현을 했습니다.
우선 이 문제를 DP 로 풀 수 있는 이유는, 게임의 진행이 무조건 오른쪽, 아래쪽으로 진행하는 방향이 있기 떄문입니다.
만약에 상하좌우라면, 아래의 코드로는 풀 수 없습니다.
제가 처음에 풀었던 오류 코드입니다.
아래의 코드는 3가지 로직을 빠트렸습니다.
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
//오른쪽으로 이동
int nr = i;
int nc = j + arr[i][j];
if(nr >= 0 && nr < N && nc >= 0 && nc < N) {
dp[nr][nc] += 1;
}
//아래쪽으로 이동
nr = i + arr[i][j];
nc = j;
if(nr >= 0 && nr < N && nc >= 0 && nc < N) {
dp[nr][nc] += 1;
}
}
}
- 만약에 if(arr[i][j] == 0 ) break; 구문이 빠졌습니다.
- 해당 구문을 통하여 종료시키지 않을경우, 이동이 되지 않아서 같은 위치에 더하게 됩니다.
- 아래의 코드에서 볼 수 있듯이 dp[nr][nc] 에 중복으로 +1을 하게되어 오류가 납니다.
- dp[i][j] > 0 구문이 빠졌습니다.
- 해당 구문을 통하여 기존에 도착할 수 있는 지점만 작동하도록 처리하여야, 해당 경로를 들어왔다고 판단할 수 있습니다.
- dp[i][j] > 0 구문을 통하여 이전에 도착한 적이 있다는 처리를 해야합니다.
- dp[nr][nc] += 1이 아닌 dp[nr][nc] += dp[i][j] 가 되어야합니다.
- 해당 코드를 통하여 이전 위치에 올 수 있는 모든 경우의 수를 더해야합니다. 이유는, dp[i][j] ( 해당 위치로 올 수 있는 경우의 수) 가 5가지가 있다고한다면, dp[i][j] 에서 dp[nr][nc]로 이동하였을때 dp[nr][nc] 로 올 수 있는 경우의 수도 5가지가 더해질 것 입니다.
- 위 로직을 통하여 다른 점에서 dp[nr][nc]로 올 수 있는 모든 경로의 수를 구할 수 있습니다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.*;
public class Main {
public static int T, N, M;
public static int answer = 0;
public static int[][] arr;
public static long[][] dp;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
arr = new int[N][N];
dp = new long[N][N];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
dp[0][0] = 1;
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(arr[i][j] == 0) break;
//오른쪽으로 이동
int nr = i;
int nc = j + arr[i][j];
if(dp[i][j] > 0 && nr >= 0 && nr < N && nc >= 0 && nc < N) {
dp[nr][nc] += dp[i][j];
}
//아래쪽으로 이동
nr = i + arr[i][j];
nc = j;
if(dp[i][j] > 0 && nr >= 0 && nr < N && nc >= 0 && nc < N) {
dp[nr][nc] += dp[i][j];
}
}
}
System.out.println(dp[N-1][N-1]);
}
}
정답코드2입니다.
아래의 오답코드를 작성하고 확인해보니, 엄청나게 단순한 오류를 찾았습니다.
바로, 해당 칸이 0 일 수 있다는 것 입니다. 만약 0 이라면 계속해서 해당 점을 DFS로 연산을 하겠지요..
아래의 오답코드들이 모두 이 오류떄문에 발생했습니다.
이 코드만 추가해주면 바로 해결됩니다.
if(matrix[r][c] == 0) return 0;
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, A, B, X, L, R, C, n, k, m, l, K, D;
static long answer = 0;
static int[][] matrix = new int[101][101];
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
for(int i=0;i<N; i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N; j++) {
matrix[i][j] = Integer.parseInt(st.nextToken());
}
}
for(int i=0;i<101; i++) {
Arrays.fill(cache[i], -1);
}
answer = DFS(0, 0);
System.out.println(answer);
}
static long[][] cache = new long[101][101];
static long DFS(int r, int c) {
if(r == N-1 && c == N -1) {
return 1;
}
if(matrix[r][c] == 0) return 0;
if(cache[r][c] != -1) return cache[r][c];
long ret = 0;
int value = matrix[r][c];
for(int[] dxdr : new int[][] { {1, 0}, {0, 1}} ) {
int nr = r + value * dxdr[0];
int nc = c + value * dxdr[1];
if(nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
ret += DFS(nr, nc);
}
return cache[r][c] = ret;
}
}
오답코드 1 입니다.
시간초과 및 메모리초과 나는 재귀와 메모이제이션을 활용한 코드입니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, A, B, X, L, R, C, n, k, m, l, K, D;
static long answer = 0;
static int[][] matrix = new int[101][101];
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
for(int i=0;i<N; i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N; j++) {
matrix[i][j] = Integer.parseInt(st.nextToken());
}
}
for(int i=0;i<101; i++) {
Arrays.fill(cache[i], -1);
}
answer = DFS(0, 0);
System.out.println(answer);
}
static long[][] cache = new long[101][101];
static long DFS(int r, int c) {
// System.out.println("R:"+r+"C:"+c);
if(r == N-1 && c == N -1) {
return 1;
}
if(cache[r][c] != -1) return cache[r][c];
long ret = 0;
int value = matrix[r][c];
for(int[] dxdr : new int[][] { {1, 0}, {0, 1}} ) {
int nr = r + value * dxdr[0];
int nc = c + value * dxdr[1];
if(nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
ret += DFS(nr, nc);
}
return cache[r][c] = ret;
}
}
오답코드2입니다.
오답코드1에서 로직이 너무 복잡해서 그런가 해서 좀 더 간소화했지만, 이제 시간초과는 해결되었지만 메모리초과가 발생합니다.
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, A, B, X, L, R, C, n, k, m, l, K, D;
static long answer = 0;
static int[][] matrix = new int[101][101];
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
for(int i=0;i<N; i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N; j++) {
matrix[i][j] = Integer.parseInt(st.nextToken());
}
}
for(int i=0;i<101; i++) {
Arrays.fill(cache[i], -1);
}
answer = DFS(0, 0);
System.out.println(answer);
}
static long[][] cache = new long[101][101];
static long DFS(int r, int c) {
if(r == N-1 && c == N -1) {
return 1;
}
if(r < 0 || r >= N || c < 0 || c >= N) {
return 0;
}
if(cache[r][c] != -1) return cache[r][c];
long ret = 0;
int value = matrix[r][c];
ret += DFS(r + value, c);
ret += DFS(r, c + value);
return cache[r][c] = ret;
}
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 10844 쉬운 계단 수 - DP JAVA (0) | 2023.08.19 |
---|---|
[백준] 2156 포도주 시식 - 동적계획법(DP, Dynamic Programming) JAVA (0) | 2023.08.19 |
[백준] 9465 스티커 - DP JAVA (0) | 2023.08.19 |
[백준] 11055 가장 큰 증가하는 부분 수열 - 동적계획법(Dynamic Programming, DP) + LIBS(Longest Increasing Biggest Subsequence) JAVA (0) | 2023.08.19 |
[백준] 1912 연속합 - DP(동적계획법) + 분할정복(DivideAndConquer) JAVA (0) | 2023.08.18 |