Open In App

Maximizing Merit Points in Training Program

Last Updated : 11 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Geek is going for a training program. He can perform any of these activities: Running, Fighting, and Learning Practice. Each activity has some point on each day. As Geek wants to improve all his skills, he can't do the same activity on two consecutive days. Help Geek to maximize his merit points as we are given a 2D array of points mat[][], corresponding to each day and activity.

Examples:

Input: mat[][] = [[1, 2, 5],
[3, 1, 1],
[3, 3, 3]]
Output: 11
Explanation: Geek will learn a new move and earn 5 point then on second day he will do running and earn 3 point and on third day he will do fighting and earn 3 points so, maximum point is 11.

Input: mat[][] = [[1, 10, 5],
[7, 0, 3],
[5, 10, 0],
[10, 2, 4]]
Output: 37
Explanation: Geek will fight and earn 10 points. On the second day, he will run and earn 7 points. On the third day, he will fight again and earn 10 points, and on the fourth day, he will run, earning another 10 points. Therefore, the maximum points are 10 + 7 + 10 + 10 = 37 points

Using Recursion - O(2^n) Time and O(n) Space

We can identify a recursive pattern in this problem. There are two state variables:

  • i: Current row index.
  • prev: Column index of the action chosen in the previous row (initialized to -1).

Recursive Steps:

  • For each column j (0 to 2), if j != prev, calculate:
    • maxi = max(maxi, mat[i][j] + calculateMaximumPoints(i+1, j, mat))
  • Base Case: If i == n, return 0 (no more rows to process).

Recurrence Relation:

  • calculateMaximumPoints(i, prev, mat) = max(mat[i][j] + calculateMaximumPoints(i+1, j, mat)) where j != prev

Base Case: If i == n (all elements have been processed), return 0

C++
// C++ code for maximizing points
// using recursion
#include <bits/stdc++.h>
using namespace std;

// Function to calculate the maximum points recursively
int calculateMaximumPoints(int i, int prev,
                           vector<vector<int>> &mat) {
  
    int n = mat.size();
  
    // Base case: if we have processed all rows, return 0
    if (i >= n)
        return 0;

    // Variable to store the maximum points for this row
    int maxi = 0; 
  
    // Iterate over the 3 possible actions (columns)
    for (int j = 0; j < 3; j++) {
      
        // Ensure the current action is not the 
        // same as the previous one
        if (j != prev) 
            maxi = max(maxi, mat[i][j] + 
                     calculateMaximumPoints(i + 1, j, mat));
    }
    return maxi; 
}

// Wrapper function to start the recursive process
int maximumPoints(vector<vector<int>> &mat) {
  
    return calculateMaximumPoints(0, -1, mat); 
}

int main() {
   
    vector<vector<int>> mat = {{1, 2, 5}, 
                               {3, 1, 1}, 
                               {3, 3, 3}};
    cout << maximumPoints(mat);

    return 0;
}
Java
// Java code for maximizing points
// using recursion

class GfG {

    // Function to calculate the maximum points recursively
    static int calculateMaximumPoints(int i, int prev,
                                      int[][] mat) {

        int n = mat.length;

        // Base case: if we have processed all rows, return
        // 0
        if (i >= n)
            return 0;

        // Variable to store the maximum points for this row
        int maxi = 0;

        // Iterate over the 3 possible actions (columns)
        for (int j = 0; j < 3; j++) {

            // Ensure the current action is not the
            // same as the previous one
            if (j != prev)
                maxi = Math.max(maxi,mat[i][j] + 
                              calculateMaximumPoints(i + 1, j, mat));
        }
        return maxi;
    }

    // Wrapper function to start the recursive process
    static int maximumPoints(int[][] mat) {

        return calculateMaximumPoints(0, -1, mat);
    }

    public static void main(String[] args) {

        int[][] mat
            = { { 1, 2, 5 }, { 3, 1, 1 }, { 3, 3, 3 } };
        System.out.println(maximumPoints(mat));
    }
}
Python
# Python code for maximizing points
# using recursion

# Function to calculate the maximum points recursively
def calculateMaximumPoints(i, prev, mat):
    n = len(mat)

    # Base case: if we have processed all rows, return 0
    if i >= n:
        return 0

    # Variable to store the maximum points for this row
    maxi = 0

    # Iterate over the 3 possible actions (columns)
    for j in range(3):
      
        # Ensure the current action is not the
        # same as the previous one
        if j != prev:
            maxi = max(maxi, mat[i][j] + calculateMaximumPoints(i + 1, j, mat))

    return maxi


def maximumPoints(mat):
    return calculateMaximumPoints(0, -1, mat)


if __name__ == "__main__":
    mat = [[1, 2, 5],
           [3, 1, 1],
           [3, 3, 3]]
    print(maximumPoints(mat))
C#
// C# code for maximizing points
// using recursion
using System;

class GfG {

    // Function to calculate the maximum points recursively
    static int CalculateMaximumPoints(int i, int prev,
                                      int[, ] mat) {
        int n = mat.GetLength(0);

        // Base case: if we have processed all rows, return
        // 0
        if (i >= n)
            return 0;

        // Variable to store the maximum points for this row
        int maxi = 0;

        // Iterate over the 3 possible actions (columns)
        for (int j = 0; j < 3; j++) {

            // Ensure the current action is not the
            // same as the previous one
            if (j != prev)
                maxi = Math.Max(maxi, mat[i, j] 
                              + CalculateMaximumPoints(i + 1, j, mat));
        }
        return maxi;
    }

    // Wrapper function to start the recursive process
    static int MaximumPoints(int[, ] mat) {

        return CalculateMaximumPoints(0, -1, mat);
    }

    static void Main(string[] args) {

        int[, ] mat
            = { { 1, 2, 5 }, { 3, 1, 1 }, { 3, 3, 3 } };
        Console.WriteLine(MaximumPoints(mat));
    }
}
JavaScript
// JavaScript code for maximizing points
// using recursion

// Function to calculate the maximum points recursively
function calculateMaximumPoints(i, prev, mat) {

    const n = mat.length;

    // Base case: if we have processed all rows, return 0
    if (i >= n)
        return 0;

    // Variable to store the maximum points for this row
    let maxi = 0;

    // Iterate over the 3 possible actions (columns)
    for (let j = 0; j < 3; j++) {
    
        // Ensure the current action is not the
        // same as the previous one
        if (j !== prev) {
            maxi = Math.max(maxi, mat[i][j] + 
            calculateMaximumPoints(i + 1, j, mat));
        }
    }
    return maxi;
}

// Wrapper function to start the recursive process
function maximumPoints(mat) {

    return calculateMaximumPoints(0, -1, mat);
}

// Driver code
const mat = [ [ 1, 2, 5 ], [ 3, 1, 1 ], [ 3, 3, 3 ] ];

console.log(maximumPoints(mat));

Output
11

Using Top-Down Dp (Memoization) - O(n) Time and O(n) Space

The recursive solution can be optimized using Dynamic Programming (DP) due to the following properties:

Optimal Substructure: The solution for a state, calculateMaximumPoints(i, prev), depends on the results of subproblems:

  • calculateMaximumPoints(i + 1, j) for valid actions j (j != prev).

Overlapping Subproblems: Many states, such as calculateMaximumPoints(2, 0), are recomputed multiple times in recursion from calculateMaximumPoints(1, 1) and calculateMaximumPoints(1, 2) . This can be avoided by memoization.

  • There are two parameter that change in the recursive solution: i going from 0 to n-1prev going from 0 to 2. So we create a 2D array memo[][] of size n*3 for memoization.
  • We initialize this array as -1 to indicate nothing is computed initially.
  • Now we modify our recursive solution to first check if the value is -1, then only make recursive calls. This way, we avoid re-computations of the same subproblems.
C++
// C++ code for maximizing points
// using memoization
#include <bits/stdc++.h>
using namespace std;

// Function to calculate the maximum points recursively
// with memoization to avoid redundant calculations
int calculateMaximumPoints(int i, int prev, 
				vector<vector<int>> &mat, vector<vector<int>> &memo) {
    int n = mat.size();

    // Base case: If all rows are processed, return 0
    if (i >= n)
        return 0;

    // If result is already computed for this state, return it
    if (prev != -1 && memo[i][prev] != -1)
        return memo[i][prev];

    int maxi = 0;

    // Iterate over all possible actions (columns)
    for (int j = 0; j < 3; j++) {

        // Skip the previous action to avoid repeating
        // the same task
        if (j != prev)
            maxi = max(maxi, mat[i][j] + 
					calculateMaximumPoints(i + 1, j, mat, memo));
    }

    // Save the computed result in memo for reuse
    if (prev != -1)
        memo[i][prev] = maxi;

    return maxi;
}

int maximumPoints(vector<vector<int>> &mat) {

    int n = mat.size();
    vector<vector<int>> memo(n, vector<int>(3, -1));
    return calculateMaximumPoints(0, -1, mat, memo);
}

int main() {

    vector<vector<int>> mat = {{1, 2, 5}, {3, 1, 1}, {3, 3, 3}};
    cout << maximumPoints(mat);
    return 0;
}
Java
// Java code for maximizing points
// using memoization
import java.util.Arrays;

class GfG {

    // Function to calculate the maximum points recursively
    // with memoization to avoid redundant calculations
    static int calculateMaximumPoints(int i, int prev,
                                      int[][] mat,
                                      int[][] memo) {

        int n = mat.length;

        // Base case: If all rows are processed, return 0
        if (i >= n)
            return 0;

        // If result is already computed for this state,
        // return it
        if (prev != -1 && memo[i][prev] != -1)
            return memo[i][prev];

        int maxi = 0;

        // Iterate over all possible actions (columns)
        for (int j = 0; j < 3; j++) {
          
            // Skip the previous action to avoid repeating
            // the same task
            if (j != prev)
                maxi = Math.max(maxi, mat[i][j] + 
                              calculateMaximumPoints(i + 1, j, mat, memo));
        }

        // Save the computed result in memo for reuse
        if (prev != -1)
            memo[i][prev] = maxi;

        return maxi;
    }


    static int maximumPoints(int[][] mat) {

        int n = mat.length;
        int[][] memo = new int[n][3];
        for (int[] row : memo)
            Arrays.fill(row, -1);
        return calculateMaximumPoints(0, -1, mat, memo);
    }

    public static void main(String[] args) {

        int[][] mat
            = { { 1, 2, 5 }, { 3, 1, 1 }, { 3, 3, 3 } };
        System.out.println(maximumPoints(mat));
    }
}
Python
# Python code for maximizing points
# using memoization

def calculateMaximumPoints(i, prev, mat, memo):
    n = len(mat)

    # Base case: If all rows are processed, return 0
    if i >= n:
        return 0

    # If result is already computed for this state, return it
    if prev != -1 and memo[i][prev] != -1:
        return memo[i][prev]

    maxi = 0

    # Iterate over all possible actions (columns)
    for j in range(3):
      
        # Skip the previous action to avoid repeating the same task
        if j != prev:
            maxi = max(maxi, mat[i][j] + calculateMaximumPoints(i + 1, j, mat, memo))

    # Save the computed result in memo for reuse
    if prev != -1:
        memo[i][prev] = maxi

    return maxi


def maximumPoints(arr):
    n = len(mat)
    memo = [[-1] * 3 for _ in range(n)]
    return calculateMaximumPoints(0, -1, mat, memo)


# Driver code
mat = [[1, 2, 5], [3, 1, 1], [3, 3, 3]]
print(maximumPoints(mat))
C#
// C# code for maximizing points
// using memoization
using System;

class GfG {
    static int CalculateMaximumPoints(int i, int prev,
                                      int[][] mat,
                                      int[, ] memo) {

        int n = mat.Length;

        // Base case: If all rows are processed, return 0
        if (i >= n)
            return 0;

        // If result is already computed for this state,
        // return it
        if (prev != -1 && memo[i, prev] != -1)
            return memo[i, prev];

        int maxi = 0;

        // Iterate over all possible actions (columns)
        for (int j = 0; j < 3; j++) {
          
            // Skip the previous action to avoid repeating
            // the same task
            if (j != prev)
                maxi = Math.Max(maxi, mat[i][j] + 
                              CalculateMaximumPoints(i + 1, j, mat, memo));
        }

        // Save the computed result in memo for reuse
        if (prev != -1)
            memo[i, prev] = maxi;

        return maxi;
    }

    static int MaximumPoints(int[][] mat) {

        int n = mat.Length;
        int[, ] memo = new int[n, 3];

        // Initialize memo with -1
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 3; j++) {
                memo[i, j] = -1;
            }
        }

        return CalculateMaximumPoints(0, -1, mat, memo);
    }

    static void Main(string[] args) {

        int[][] mat = new int[][] { new int[] { 1, 2, 5 },
                                    new int[] { 3, 1, 1 },
                                    new int[] { 3, 3, 3 } };

        Console.WriteLine(MaximumPoints(mat));
    }
}
JavaScript
// JavaScript code for maximizing points
// using memoization

function calculateMaximumPoints(i, prev, mat, memo) {

    const n = mat.length;

    // Base case: If all rows are processed, return 0
    if (i >= n)
        return 0;

    // If result is already computed for this state, return
    // it
    if (prev !== -1 && memo[i][prev] !== -1)
        return memo[i][prev];

    let maxi = 0;

    // Iterate over all possible actions (columns)
    for (let j = 0; j < 3; j++) {
    
        // Skip the previous action to avoid repeating the
        // same task
        if (j !== prev) {
            maxi = Math.max(maxi, mat[i][j] + 
            calculateMaximumPoints(i + 1, j, mat, memo));
        }
    }

    // Save the computed result in memo for reuse
    if (prev !== -1)
        memo[i][prev] = maxi;

    return maxi;
}

function maximumPoints(mat) {

    const n = mat.length;
    const memo
        = Array.from({length : n}, () => Array(3).fill(-1));
    return calculateMaximumPoints(0, -1, mat, memo);
}

// Driver code
const mat = [ [ 1, 2, 5 ], [ 3, 1, 1 ], [ 3, 3, 3 ] ];

console.log(maximumPoints(mat));

Output
11

Using Bottom-Up Dp (Tabulation) - O(n) Time and O(n) Space

We create a 2D array dp[n+1][3], where dp[i][j] represents the maximum points achievable from row i with action j.

We fill the dp array as follows:

  • We initialize all values of dp[i][j] to 0 because initially, there are no points.
  • Iterate over the rows of mat[][] from bottom to top (i.e., from row n-1 to row 0).
  • For each row i, iterate over all possible actions j (i.e., 0, 1, and 2).

For each action j:

  • We check all possible actions k in the next row i+1.
  • If k is not equal to j (i.e., avoid repeating the same action), we update dp[i][j] as follows: dp[i][j] = max(dp[i][j], dp[i+1][k])
  • After considering all possible actions for row i, we add the points from the current row: dp[i][j] += mat[i][j]

This can be explained as there are two cases: either we take the element mat[i][j] or we don’t. We take an element only when it’s allowed, meaning we can’t repeat the same action (j). The value in dp[i][j] is calculated by considering the maximum points we can get from the next row i+1 with a different action k, then adding the current row's points mat[i][j]. The final answer is the maximum value from dp[0][0], dp[0][1], and dp[0][2].

C++
// C++ code for maximizing points
// using tabulation
#include <bits/stdc++.h>
using namespace std;

// Function to compute maximum points using tabulation
int maximumPoints(vector<vector<int>> &mat) {

    int n = mat.size();

    // dp[i][j] stores the maximum points we can achieve
    // starting from row i with action j
    vector<vector<int>> dp(n + 1, vector<int>(3));

    // Fill dp array starting from the last row
    for (int i = n - 1; i >= 0; i--) {

        // Iterate over all actions for the current row
        for (int j = 0; j < 3; j++) {

            // Try all possible actions for the next row
            // (k) but avoid the same action
            for (int k = 0; k < 3; k++) {

                if (k == j)
                    continue;
              
                // Max points from the next row with action k
                dp[i][j] = max(dp[i][j], dp[i + 1][k]);
            }
          
            // Add the current row's points
            dp[i][j] += mat[i][j];
        }
    }

    // Return the maximum points possible from the first
    // row with any action (0, 1, or 2)
    return max({dp[0][0], dp[0][1], dp[0][2]});
}

int main() {

    vector<vector<int>> mat = {{1, 2, 5}, {3, 1, 1}, {3, 3, 3}};
    cout << maximumPoints(mat);

    return 0;
}
Java
// Java code for maximizing points
// using tabulation
class GfG {

    // Function to compute maximum points using tabulation
    static int maximumPoints(int[][] mat) {

        int n = mat.length;

        // dp[i][j] stores the maximum points we can achieve
        // starting from row i with action j
        int[][] dp = new int[n + 1][3];

        // Fill dp array starting from the last row
        for (int i = n - 1; i >= 0; i--) {

            // Iterate over all actions for the current row
            for (int j = 0; j < 3; j++) {

                // Try all possible actions for the next row
                // (k) but avoid the same action
                for (int k = 0; k < 3; k++) {

                    if (k == j)
                        continue;
                  
                    // Max points from the next row with
                    // action k
                    dp[i][j]
                        = Math.max(dp[i][j], dp[i + 1][k]);
                }
              
                // Add the current row's points
                dp[i][j] += mat[i][j];
            }
        }

        // Return the maximum points possible from the first
        // row with any action (0, 1, or 2)
        return Math.max(Math.max(dp[0][0], dp[0][1]),
                        dp[0][2]);
    }

    public static void main(String[] args) {
        int[][] mat
            = { { 1, 2, 5 }, { 3, 1, 1 }, { 3, 3, 3 } };
        System.out.println(maximumPoints(mat));
    }
}
Python
# Python code for maximizing points
# using tabulation

def maximumPoints(mat):
    n = len(mat)

    # dp[i][j] stores the maximum points we can achieve
    # starting from row i with action j
    dp = [[0] * 3 for _ in range(n + 1)]

    # Fill dp array starting from the last row
    for i in range(n - 1, -1, -1):
        for j in range(3):

            # Try all possible actions for the next row
            # (k) but avoid the same action
            for k in range(3):
                if k == j:
                    continue
                    
                # Max points from the next row with action k
                dp[i][j] = max(dp[i][j], dp[i + 1][k])

            # Add the current row's points
            dp[i][j] += mat[i][j]

    # Return the maximum points possible from the first
    # row with any action (0, 1, or 2)
    return max(dp[0][0], dp[0][1], dp[0][2])


mat = [[1, 2, 5],
       [3, 1, 1],
       [3, 3, 3]]
print(maximumPoints(mat))
C#
// C# code for maximizing points
// using tabulation
using System;

class GfG {

    // Function to compute maximum points using tabulation
    static int maximumPoints(int[, ] mat) {

        int n = mat.GetLength(0);

        // dp[i][j] stores the maximum points we can achieve
        // starting from row i with action j
        int[, ] dp = new int[n + 1, 3];

        // Fill dp array starting from the last row
        for (int i = n - 1; i >= 0; i--) {

            // Iterate over all actions for the current row
            for (int j = 0; j < 3; j++) {

                // Try all possible actions for the next row
                // (k) but avoid the same action
                for (int k = 0; k < 3; k++) {

                    if (k == j)
                        continue;
                  
                    // Max points from the next row with
                    // action k
                    dp[i, j]
                        = Math.Max(dp[i, j], dp[i + 1, k]);
                }
              
                // Add the current row's points
                dp[i, j] += mat[i, j];
            }
        }

        // Return the maximum points possible from the first
        // row with any action (0, 1, or 2)
        return Math.Max(Math.Max(dp[0, 0], dp[0, 1]),
                        dp[0, 2]);
    }

    static void Main(string[] args) {
        int[, ] mat
            = { { 1, 2, 5 }, { 3, 1, 1 }, { 3, 3, 3 } };
        Console.WriteLine(maximumPoints(mat));
    }
}
JavaScript
// JavaScript code for maximizing points
// using tabulation

function maximumPoints(mat) {
    const n = mat.length;

    // dp[i][j] stores the maximum points we can achieve
    // starting from row i with action j
    const dp
        = Array.from({length : n + 1}, () => [0, 0, 0]);

    // Fill dp array starting from the last row
    for (let i = n - 1; i >= 0; i--) {
        for (let j = 0; j < 3; j++) {

            // Try all possible actions for the next row
            // (k) but avoid the same action
            for (let k = 0; k < 3; k++) {
                if (k === j)
                    continue;
                    
                // Max points from the next row with action
                // k
                dp[i][j] = Math.max(dp[i][j], dp[i + 1][k]);
            }
            
            // Add the current row's points
            dp[i][j] += mat[i][j];
        }
    }

    // Return the maximum points possible from the first
    // row with any action (0, 1, or 2)
    return Math.max(dp[0][0], dp[0][1], dp[0][2]);
}

// Driver code
const mat = [ [ 1, 2, 5 ], [ 3, 1, 1 ], [ 3, 3, 3 ] ];
console.log(maximumPoints(mat));

Output
11

Next Article

Similar Reads