프로그래밍 문제 해결에서 "체리 픽업 II"는 두 로봇이 체리를 수집하는 과정을 통해 최적화 문제를 해결하는 독특한 도전입니다. 이 문제는 로봇이 주어진 그리드 위를 이동하면서 체리를 수집하되, 특정 규칙을 따라야 한다는 설정을 가지고 있습니다.
문제 이해 🤔
주어진 rows x cols
크기의 그리드에서, grid[i][j]
는 (i, j) 셀에서 수집할 수 있는 체리의 수를 나타냅니다. 두 로봇은 각각 그리드의 상단 왼쪽 모서리(0, 0)와 상단 오른쪽 모서리(0, cols - 1)에 위치하며, 다음 규칙을 따라 움직입니다:
- 셀 (i, j)에서 로봇은 셀 (i + 1, j - 1), (i + 1, j), 또는 (i + 1, j + 1)로 이동할 수 있습니다.
- 로봇이 셀을 지나가면, 그 셀의 모든 체리를 수집하고, 해당 셀은 빈 셀이 됩니다.
- 두 로봇이 같은 셀에 도착할 경우, 체리는 한 로봇만 수집할 수 있습니다.
- 로봇은 그리드 밖으로 이동할 수 없습니다.
- 두 로봇 모두 그리드의 하단 행에 도달해야 합니다.
이 문제의 목표는 두 로봇이 수집할 수 있는 체리의 최대 수를 반환하는 것입니다.
예시 😮
예시 #1
- 입력: grid = [[3,1,1],[2,5,1],[1,5,5],[2,1,1]]
- 출력: 24 설명: 로봇 #1과 #2의 경로는 각각 녹색과 파란색으로 설명되어 있습니다.
- 로봇 #1이 수집한 체리, (3 + 2 + 5 + 2) = 12.
- 로봇 #2가 수집한 체리, (1 + 5 + 5 + 1) = 12.
- 체리의 총 합: 12 + 12 = 24.
예시 #2
- 입력: grid = [[1,0,0,0,0,0,1],[2,0,0,0,0,3,0],[2,0,9,0,0,0,0],[0,3,0,5,4,0,0],[1,0,2,3,0,0,6]]
- 출력: 28 설명: 로봇 #1과 #2의 경로는 각각 녹색과 파란색으로 설명되어 있습니다.
- 로봇 #1이 수집한 체리, (1 + 9 + 5 + 2) = 17.
- 로봇 #2가 수집한 체리, (1 + 3 + 4 + 3) = 11.
- 체리의 총 합: 17 + 11 = 28.
해결 전략 📝
이 문제를 해결하기 위해서는 먼저 각 단계에서의 최적의 선택을 결정할 수 있는 동적 프로그래밍 방식을 고려해야 합니다. 각 로봇의 위치에 따라 가능한 모든 이동 경로를 고려하고, 각 단계에서 최적의 수집량을 갱신해 나가는 방식을 사용합니다.
1. 동적 프로그래밍 테이블 정의
각 단계에서, 우리는 두 로봇이 선택할 수 있는 모든 이동 경로를 고려합니다. 로봇은 현재 위치에서 좌측 대각선 하단(c-1
), 바로 아래(c
), 우측 대각선 하단(c+1
)으로 이동할 수 있습니다. 이 중에서, 두 로봇이 같은 셀에 도착하게 되면 체리는 한 번만 수집되므로, 이 경우의 수집량을 조정해야 합니다. 각각의 가능한 이동 조합에 대해, 우리는 해당 이동이 가져올 수 있는 최대 체리 수를 계산하고, 이 중 최대값을 현재 위치의 최적 해로 선택합니다.
2. 최적의 이동 경로 계산
각 단계에서, 우리는 두 로봇이 선택할 수 있는 모든 이동 경로를 고려합니다. 로봇은 현재 위치에서 좌측 대각선 하단(c-1
), 바로 아래(c
), 우측 대각선 하단(c+1
)으로 이동할 수 있습니다. 이 중에서, 두 로봇이 같은 셀에 도착하게 되면 체리는 한 번만 수집되므로, 이 경우의 수집량을 조정해야 합니다. 각각의 가능한 이동 조합에 대해, 우리는 해당 이동이 가져올 수 있는 최대 체리 수를 계산하고, 이 중 최대값을 현재 위치의 최적 해로 선택합니다.
3. 최종 결과 도출
모든 행을 순회한 후, 우리는 dp
배열의 마지막 행, 즉 dp[마지막 행][*][*]
에 저장된 값들 중에서 최댓값을 찾습니다. 이 값은 두 로봇이 그리드를 통해 이동하면서 수집할 수 있는 체리의 최대 총량을 나타냅니다. 최종적으로, 이 최댓값이 문제의 해답이 됩니다.
구현 코드 🚀
public class CherryPickupII {
public int cherryPickup(int[][] grid) {
int R = grid.length, C = grid[0].length;
int[][][] dp = new int[R][C][C]; // 1. 동적 프로그래밍 테이블 정의
for (int[][] layer : dp) {
for (int[] row : layer) {
Arrays.fill(row, -1); // 초기값을 -1로 설정하여 아직 계산되지 않음을 표시
}
}
return dfs(grid, dp, 0, 0, C - 1); // 첫 번째 위치에서 탐색 시작
}
private int dfs(int[][] grid, int[][][] dp, int r, int c1, int c2) {
int R = grid.length, C = grid[0].length;
if (c1 < 0 || c2 < 0 || c1 >= C || c2 >= C) return 0; // 범위를 벗어나는 경우 0 반환
if (dp[r][c1][c2] != -1) return dp[r][c1][c2]; // 이미 계산된 경우 해당 값을 반환
int result = grid[r][c1]; // 현재 위치에서 체리 수집
if (c1 != c2) result += grid[r][c2]; // 두 로봇이 다른 셀에 있으면, 두 번째 로봇의 체리도 추가
// 2. 최적의 이동 경로 계산: 다음 행으로 이동하는 모든 경우를 고려
if (r < R - 1) {
int max = 0;
for (int newC1 = c1 - 1; newC1 <= c1 + 1; newC1++) {
for (int newC2 = c2 - 1; newC2 <= c2 + 1; newC2++) {
max = Math.max(max, dfs(grid, dp, r + 1, newC1, newC2)); // 모든 가능한 이동에 대해 최대 체리 수를 계산
}
}
result += max; // 최대 체리 수를 현재 결과에 추가
}
dp[r][c1][c2] = result; // 계산된 결과를 저장
return result; // 현재 위치에서의 최대 체리 수를 반환
}
public static void main(String[] args) {
CherryPickupII solution = new CherryPickupII();
int[][] grid = {
{0,8,7,10,9,10,0,9,6},
{8,7,10,8,7,4,9,6,10},
{8,1,1,5,1,5,5,1,2},
{9,4,10,8,8,1,9,5,0},
{4,3,6,10,9,2,4,8,10},
{7,3,2,8,3,3,5,9,8},
{1,2,6,5,6,2,0,10,0}
};
int result = solution.cherryPickup(grid);
System.out.println(result); // 출력: 최대로 수집할 수 있는 체리의 수
}
}
이 문제의 핵심은 각 단계에서 최적의 선택을 결정하고, 그 선택들을 통해 최종적으로 최대의 결과를 도출하는 것입니다. 동적 프로그래밍을 통해 복잡한 문제를 단순화하고 효율적으로 해결할 수 있음을 보여줍니다.
마무리 🌟
"체리 픽업 II" 문제는 동적 프로그래밍 기술을 활용하여 복잡한 최적화 문제를 해결하는 데 있어 좋은 연습이 됩니다. 이 문제를 통해 학습된 기술은 다양한 알고리즘 문제 해결에 적용될 수 있습니다. Happy coding! 👩💻👨💻
'개발 공부 > LeetCode' 카테고리의 다른 글
451. Sort Characters By Frequency (33) | 2024.02.07 |
---|---|
387. First Unique Character in a String (45) | 2024.02.05 |