Open In App

Maximizing Chocolates in Grid (Chocolates Pickup II)

Last Updated : 09 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given a 2D Matrix grid[][] of NxN size, some cells are blocked, and the remaining unblocked cells have chocolate in them. If grid[i][j] == -1, then the cell is blocked, else grid[i][j] is the number of chocolates present in cell (i, j). Find the maximum number of chocolates that you can collect by moving from cell (0, 0) to cell (N-1, N-1) and then back to cell (0, 0). While moving from cell (0, 0) to cell (N-1, N-1), only allowed moves are: down: (i, j) to (i+1, j) or right: (i, j) to (i, j+1) and while moving back from cell (N-1, N-1) to cell (0, 0), only allowed moves are up: (i, j) to (i-1, j) or left: (i, j) to (i, j-1).

Note: After collecting the chocolates from a cell(i, j), the cell becomes empty i.e. grid[i][j] becomes 0.

Examples:

Input: grid = [[0, 1, -1], [1, 0, -1], [1, 1, 1]]
Output: 5
Explanation: It is clearly visible that player the player has gone down -> down -> right-> right -> left -> up -> up-> left and collected total 5 chocolates in the path.

chocolates

Input: grid = [[1, 1, 0], [1, 1, 1], [0, 1, 1]]
Output: 7
Explanation: In the above illustration it is clearly visible that player the player has gone down -> down -> right-> right -> up -> left -> up -> left and collected total 7 chocolates in the path.

chocolates-(1)

Input: grid = [[1, 1, -1], [1, -1, 1], [-1, 1, 1]]
Output: 0
Explanation: There is no possible path to reach the bottom right (n-1, n-1) cell hence we cannot collect any chocolate.

It might seem that the problem can be solved by dividing the round trip into two parts: from (0, 0) to (N-1, N-1) and from (N-1, N-1) to (0, 0). Now, find the maximum chocolates we can pick up with one path from (0, 0) to (N-1, N-1), pick them up, and then find the maximum chocolates with a second path from (N-1, N-1) to (0,0) on the remaining field. But this approach will fail for the second testcase among the above examples.

Approach: To solve the problem, follow the below observations:

Instead of walking from end to beginning, let’s reverse the second leg of the path, so we can consider two persons both moving from the beginning to the end. Now, instead of considering both the paths independently we need to move both the persons simultaneously to maximize the number of chocolates. So, now we can define a function f(r1, c1, r2, c2) which returns the maximum number of chocolates if the first person has reached cell (r1, c1) and the second person has reached cell (r2, c2) which will give us a 4D DP states. We can further optimize by using the following observation:
After any step, we can say that r1 + c1 = r2 + c2. So, if we have two people at positions (r1, c1) and (r2, c2), then r2 = r1 + c1 – c2. That means the variables r1, c1, c2 uniquely determine 2 people who have walked the same r1 + c1 number of steps which will give us a 3D DP state.

Steps to solve the problem:

  • Let dp[i1][j1][j2] be the maximum number of chocolates obtained by two people starting at (i1,j1) and (i2, j2) and walking towards (N-1, N-1) picking up chocolates where i2 = i1+j1-j2.
  • If grid[i1][j1] and grid[i2][j2] are not blocked, then the value of dp[i1][j1][j2] is (grid[i1][j1] + grid[i2][j2]), plus the maximum of dp[i1+1][j1][j2], dp[i1][j1+1][j2], dp[i1+1][j1][j2+1], dp[i1][j1+1][j2+1] as appropriate. We should also be careful to not double count in case (i1, c1) == (i2, j2).
  • We say that we take the maximum of dp[r+1][c1][c2] etc. because it corresponds to the 4 possibilities for person 1 and 2 moving down and right:
    • Person 1 down and person 2 down: dp[i1+1][j1][j2]
    • Person 1 right and person 2 down: dp[i1][j1+1][j2]
    • Person 1 down and person 2 right: dp[i1+1][j1][j2+1]
    • Person 1 right and person 2 right: dp[i1][j1+1][j2+1]

Below is the Implementation of the above approach:

C++




#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <climits>
 
using namespace std;
 
// 3D array for memoization
static int dp[15][15][15];
static const int unSolved = -1;
 
// array to store directions
static int directions[][3] = {{0, 1, 1}, {0, 1, 0}, {1, 0, 0}, {1, 0, 1}};
 
// Check if a move is valid
bool isValid(int i1, int j1, int j2, int n) {
    int i2 = i1 + j1 - j2;
    return i1 >= 0 && i1 < n && i2 >= 0 && i2 < n && j1 >= 0 && j1 < n && j2 >= 0 && j2 < n;
}
 
// Recursive function to calculate the maximum chocolate
int f(int i1, int j1, int j2, vector<vector<int>>& grid) {
    int n = grid.size();
    if (i1 == j1 && j1 == j2 && i1 == n - 1) {
        return grid[i1][j1];
    }
    if (dp[i1][j1][j2] != unSolved) {
        return dp[i1][j1][j2];
    }
    int i2 = i1 + j1 - j2;
    if (grid[i1][j1] == -1 || grid[i2][j2] == -1) {
        return INT_MIN;
    }
    int maxSum = INT_MIN;
    int val = (i1 == i2 && j1 == j2) ? grid[i1][j1] : grid[i1][j1] + grid[i2][j2];
    for (auto& direction : directions) {
        int deli1 = direction[0];
        int delj1 = direction[1];
        int delj2 = direction[2];
        if (isValid(i1 + deli1, j1 + delj1, j2 + delj2, n)) {
            int currentSum = val + f(i1 + deli1, j1 + delj1, j2 + delj2, grid);
            maxSum = max(maxSum, currentSum);
        }
    }
    dp[i1][j1][j2] = maxSum;
    return maxSum;
}
 
int chocolatePickup(vector<vector<int>>& grid) {
    int n = grid.size();
    memset(dp, unSolved, sizeof(dp));
 
    int result = f(0, 0, 0, grid);
    return (result > 0) ? result : 0;
}
 
int main() {
    vector<vector<int>> grid = {{0, 1, -1}, {1, 0, -1}, {1, 1, 1}};
    int result = chocolatePickup(grid);
    cout << result << endl;
 
    return 0;
}


Java




import java.util.Arrays;
 
public class Solution {
     
      // 3D array for memoization
    static int[][][] dp;
    static int unSolved = -1;
      // array to store directions
    static int[][] directions = {{0, 1, 1}, {0, 1, 0}, {1, 0, 0}, {1, 0, 1}};
 
    // Check if a move is valid
    static boolean isValid(int i1, int j1, int j2, int n) {
        int i2 = i1 + j1 - j2;
        return i1 >= 0 && i1 < n && i2 >= 0 && i2 < n && j1 >= 0 && j1 < n && j2 >= 0 && j2 < n;
    }
 
    // Recursive function to calculate the maximum chocolate
    static int f(int i1, int j1, int j2, int[][] grid) {
        int n = grid.length;
        if (i1 == j1 && j1 == j2 && i1 == n - 1) {
            return grid[i1][j1];
        }
        if (dp[i1][j1][j2] != unSolved) {
            return dp[i1][j1][j2];
        }
        int i2 = i1 + j1 - j2;
        if (grid[i1][j1] == -1 || grid[i2][j2] == -1) {
            return Integer.MIN_VALUE;
        }
        int maxSum = Integer.MIN_VALUE;
        int val = (i1 == i2 && j1 == j2) ? grid[i1][j1] : grid[i1][j1] + grid[i2][j2];
        for (int[] direction : directions) {
            int deli1 = direction[0];
            int delj1 = direction[1];
            int delj2 = direction[2];
            if (isValid(i1 + deli1, j1 + delj1, j2 + delj2, n)) {
                int currentSum = val + f(i1 + deli1, j1 + delj1, j2 + delj2, grid);
                maxSum = Math.max(maxSum, currentSum);
            }
        }
        dp[i1][j1][j2] = maxSum;
        return maxSum;
    }
 
    public int chocolatePickup(int[][] grid) {
        int n = grid.length;
        dp = new int[n][n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                Arrays.fill(dp[i][j], unSolved);
            }
        }
 
        int result = f(0, 0, 0, grid);
        return (result > 0) ? result : 0;
    }
 
    public static void main(String[] args) {
        Solution obj = new Solution();
        int[][] grid = {{0, 1, -1}, {1, 0, -1}, {1, 1, 1}};
        int result = obj.chocolatePickup(grid);
        System.out.println(result);
    }
}
 
 
// This code is contributed by rambabuguphka


Python3




class Solution:
    def chocolatePickup(self, grid):
        n = len(grid)
         
        # Check if a move is valid
        def isValid(i1, j1, j2):
            i2 = i1+j1-j2
            return i1 >= 0 and i1 < n and i2 >= 0 and i2 < n and j1 >= 0 and j1 < n and j2 >= 0 and j2 < n
         
        # Recursive function to calculate the maximum chocolate
        def f(i1, j1, j2):
            if i1 == j1 == j2 == n-1:
                return grid[i1][j1]
            if dp[i1][j1][j2] != unSolved:
                return dp[i1][j1][j2]
            i2 = i1+j1-j2
            if grid[i1][j1] == -1 or grid[i2][j2] == -1:
                return float('-inf')
            maxi = float('-inf')
            val = grid[i1][j1] if i1 == i2 and j1 == j2 else grid[i1][j1]+grid[i2][j2]
            for deli1, delj1, delj2 in dirc:
                if isValid(i1+deli1, j1+delj1, j2+delj2):
                    curr = val + f(i1+deli1, j1+delj1, j2+delj2)
                    maxi = max(maxi, curr)
            dp[i1][j1][j2] = maxi
            return maxi
        unSolved = -1
         
        # array to store directions
        dirc = [(0, 1, 1), (0, 1, 0), (1, 0, 0), (1, 0, 1)]
         
        # 3D array for memoization
        dp = [[[unSolved]*n for _ in range(n)] for __ in range(n)]
        res = f(0, 0, 0)
        return res if res != float('-inf') else 0
 
 
obj = Solution()
grid = [[0, 1, -1], [1, 0, -1], [1, 1, 1]]
result = obj.chocolatePickup(grid)
print(result)


C#




using System;
 
public class Solution
{
    // 3D array for memoization
    static int[,,] dp;
    static int unSolved = -1;
     
    // array to store directions
    static int[,] directions = { { 0, 1, 1 }, { 0, 1, 0 }, { 1, 0, 0 }, { 1, 0, 1 } };
 
    // Check if a move is valid
    static bool IsValid(int i1, int j1, int j2, int n)
    {
        int i2 = i1 + j1 - j2;
        return i1 >= 0 && i1 < n && i2 >= 0 && i2 < n && j1 >= 0 && j1 < n && j2 >= 0 && j2 < n;
    }
 
    // Recursive function to calculate the maximum chocolate
    static int F(int i1, int j1, int j2, int[,] grid)
    {
        int n = grid.GetLength(0);
        if (i1 == j1 && j1 == j2 && i1 == n - 1)
        {
            return grid[i1, j1];
        }
        if (dp[i1, j1, j2] != unSolved)
        {
            return dp[i1, j1, j2];
        }
        int i2 = i1 + j1 - j2;
        if (grid[i1, j1] == -1 || grid[i2, j2] == -1)
        {
            return int.MinValue;
        }
        int maxSum = int.MinValue;
        int val = (i1 == i2 && j1 == j2) ? grid[i1, j1] : grid[i1, j1] + grid[i2, j2];
        for (int direction = 0; direction < directions.GetLength(0); direction++)
        {
            int deli1 = directions[direction, 0];
            int delj1 = directions[direction, 1];
            int delj2 = directions[direction, 2];
            if (IsValid(i1 + deli1, j1 + delj1, j2 + delj2, n))
            {
                int currentSum = val + F(i1 + deli1, j1 + delj1, j2 + delj2, grid);
                maxSum = Math.Max(maxSum, currentSum);
            }
        }
        dp[i1, j1, j2] = maxSum;
        return maxSum;
    }
 
    public int ChocolatePickup(int[,] grid)
    {
        int n = grid.GetLength(0);
        dp = new int[n, n, n];
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                for (int k = 0; k < n; k++)
                {
                    dp[i, j, k] = unSolved;
                }
            }
        }
 
        int result = F(0, 0, 0, grid);
        return (result > 0) ? result : 0;
    }
 
    public static void Main(string[] args)
    {
        Solution obj = new Solution();
        int[,] grid = { { 0, 1, -1 }, { 1, 0, -1 }, { 1, 1, 1 } };
        int result = obj.ChocolatePickup(grid);
        Console.WriteLine(result);
    }
}
 
// This code is contributed by akshitaguprzj3


Javascript




const unSolved = -1;
const directions = [
    [0, 1, 1],
    [0, 1, 0],
    [1, 0, 0],
    [1, 0, 1]
];
 
// Check if a move is valid
function isValid(i1, j1, j2, n) {
    const i2 = i1 + j1 - j2;
    return i1 >= 0 && i1 < n && i2 >= 0 && i2 < n && j1 >= 0 && j1 < n && j2 >= 0 && j2 < n;
}
 
// Recursive function to calculate the maximum chocolate
function f(i1, j1, j2, grid, dp) {
    const n = grid.length;
    if (i1 === j1 && j1 === j2 && i1 === n - 1) {
        return grid[i1][j1];
    }
    if (dp[i1][j1][j2] !== unSolved) {
        return dp[i1][j1][j2];
    }
    const i2 = i1 + j1 - j2;
    if (grid[i1][j1] === -1 || grid[i2][j2] === -1) {
        return Number.MIN_VALUE;
    }
    let maxSum = Number.MIN_VALUE;
    const val = (i1 === i2 && j1 === j2) ? grid[i1][j1] : grid[i1][j1] + grid[i2][j2];
    for (const direction of directions) {
        const [deli1, delj1, delj2] = direction;
        if (isValid(i1 + deli1, j1 + delj1, j2 + delj2, n)) {
            const currentSum = val + f(i1 + deli1, j1 + delj1, j2 + delj2, grid, dp);
            maxSum = Math.max(maxSum, currentSum);
        }
    }
    dp[i1][j1][j2] = maxSum;
    return maxSum;
}
 
function chocolatePickup(grid) {
    const n = grid.length;
    const dp = new Array(n).fill(null).map(() =>
    new Array(n).fill(null).map(() => new Array(n).fill(unSolved)));
 
    const result = f(0, 0, 0, grid, dp);
    return (result > 0) ? result : 0;
}
 
 
const grid = [
    [0, 1, -1],
    [1, 0, -1],
    [1, 1, 1]
];
const result = chocolatePickup(grid);
console.log(result);


Output

5







Time Complexity: O(N*N*N), where N is the number of rows or columns of the grid.
Auxiliary Space: O(N*N*N), the size of dp array



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads