동적 계획법 (Dynamic Programming)
등굣길
def solution(m, n, puddles):
dp = [[0] * (m + 1) for _ in range(n + 1)] # dp 테이블 초기화
dp[1][1] = 1
for i, j in puddles: # 웅덩이가 있는 곳은 -1로 표시
dp[j][i] = -1 # (x, y) -> x는 열(column), y는 행(row)
for i in range(1, n + 1):
for j in range(1, m + 1):
if dp[i][j] == -1: # 웅덩이이면
dp[i][j] = 0 # 이후 값에 영향을 주지 않게 하기 위해 0으로 바꾸고
continue # 건너뜀
# 위에서 오는 경우와 왼쪽에서 오는 경우를 더해줌
dp[i][j] += (dp[i - 1][j] + dp[i][j - 1]) % 1000000007
return(dp[n][m])
DP 테이블 초기화:
dp라는 이름의 2차원 리스트를 만듭니다. 이 리스트는 격자에 해당하는 크기(n+1 x m+1)로 초기화되며, 모든 값을 0으로 설정합니다. dp[1][1]을 1로 설정하여, 시작 위치에서의 경로 수를 1로 만듭니다.
웅덩이 위치 표시:
puddles 리스트에 있는 좌표를 순회하면서, 웅덩이 위치를 dp 테이블에서 -1로 표시합니다. 이때 웅덩이 위치를 dp[i][j]가 아니라 dp[j][i]로 표시하는 이유는 puddles 리스트의 좌표가 (x, y)로 주어지는데, x는 열(column)에, y는 행(row)에 해당하기 때문입니다.
경로 계산:
dp 테이블을 순회하면서 각 위치에 도달할 수 있는 경로의 개수를 계산합니다. 특정 위치가 웅덩이(-1)이면, 그 위치의 값을 0으로 바꾸고, 이후 계산에 영향을 미치지 않도록 건너뜁니다.
(0으로 바꿔서 웅덩이는 계산이 안되도록)
그렇지 않으면, 현재 위치 dp[i][j]에서 위쪽에서 오는 경로(dp[i-1][j])와 왼쪽에서 오는 경로(dp[i][j-1])를 더해줍니다. 이때 큰 수를 처리하기 위해 1000000007로 나눈 나머지를 저장합니다.
결과 반환:
마지막 위치인 dp[n][m]에 도달하는 경로의 개수를 반환합니다.
public class Solution {
public int solution(int m, int n, int[][] puddles) {
int[][] dp = new int[n + 1][m + 1]; // dp 테이블 초기화
dp[1][1] = 1;
// 웅덩이 있는 곳을 -1로 표시
for (int[] puddle : puddles) {
dp[puddle[1]][puddle[0]] = -1; // (x, y) -> x는 열(column), y는 행(row)
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (dp[i][j] == -1) { // 웅덩이이면
dp[i][j] = 0; // 이후 값에 영향을 주지 않게 하기 위해 0으로 바꾸고
continue; // 건너뜀
}
// 위에서 오는 경우와 왼쪽에서 오는 경우를 더해줌
dp[i][j] += (dp[i - 1][j] + dp[i][j - 1]) % 1000000007 ;
}
}
return dp[n][m]; // 결과 반환
}
}
Java에서 2차원 배열을 int[][] dp = new int[n + 1][m + 1];로 초기화할 때, 이 배열은 고정된 크기를 가지지만, 각 요소의 값은 언제든지 변경할 수 있습니다. 즉, 배열의 크기는 고정되지만 배열 내부의 값들은 변경 가능합니다.
// 웅덩이 있는 곳을 -1로 표시
for (int[] puddle : puddles) {
dp[puddle[1]][puddle[0]] = -1; // (x, y) -> x는 열(column), y는 행(row)
}
2차원 배열의 값을 어떻게 바꾸는지 몰랐었다...