Maximize Score After N Operations

Updated on 12 June, 2025
Maximize Score After N Operations header image

Problem Statement

You are provided with an array of integers called nums, whose length is twice a specific integer n. The task you face involves a series of n operations that you need to perform on this array. Each operation consists of selecting two elements from the array, calculating the greatest common divisor (GCD) of these two numbers, then multiplying the index of the operation (starting from one) with this GCD to obtain a score. The chosen elements are removed from the array after each operation. Your objective is to determine the order of pair selections which maximizes the total score accumulated over all operations.

Examples

Example 1

Input:

nums = [1,2]

Output:

1

Explanation:

 The optimal choice of operations is:
(1 * gcd(1, 2)) = 1

Example 2

Input:

nums = [3,4,6,8]

Output:

11

Explanation:

 The optimal choice of operations is:
(1 * gcd(3, 6)) + (2 * gcd(4, 8)) = 3 + 8 = 11

Example 3

Input:

nums = [1,2,3,4,5,6]

Output:

14

Explanation:

 The optimal choice of operations is:
(1 * gcd(1, 5)) + (2 * gcd(2, 4)) + (3 * gcd(3, 6)) = 1 + 4 + 9 = 14

Constraints

  • 1 <= n <= 7
  • nums.length == 2 * n
  • 1 <= nums[i] <= 106

Approach and Intuition

  1. The problem revolves around strategically picking pairs (x, y) from the array to maximize the score for each operation. Given the multiplication of the operation index i with the gcd(x, y) where i increases with each step, it is clear that to maximize the total score, one should aim to achieve the highest possible GCD values in the later operations, when i is larger.
  2. The operations are inherently sequential and affect each other; the choices made in earlier operations directly impact the potential pairs available for later ones. This makes the problem a good candidate for a recursive solution or dynamic programming approach with memoization, where the state could be represented by the current state of the array (or the remaining elements).
  3. Consider utilizing the properties of GCD:
    • Pairs with one or both numbers being high and sharing significant factors (e.g., multiples of each other) are strong candidates for later operations due to potentially higher GCD results.
    • Operating on smaller or prime numbers earlier might be beneficial as their GCD values with other elements are likely to be lower.
  4. Due to limited size constraints (1 <= n <= 7, leading to nums.length being at most 14), a recursive approach that tries all possible pairs in all possible sequences might be feasible with the aid of memoization to avoid recalculating results of identical subproblems excessively. This use of cache can significantly cut down the computational complexity.
  5. Calculating the GCD can be done efficiently using Euclid's algorithm, which greatly helps in optimizing the solution as GCD calculations are frequent.
  6. Finally, due to the exponential number of combinations as described but manageable constraints, a depth-first search (DFS) type of recursive exploration might be the most straightforward approach to prototype and understand the overall solution dynamics.

By applying a systematic approach to testing all valid pairs and keeping track of the maximal results for previous states, the solution can be built up to find the optimal pairing strategy that maximizes the score.

Solutions

  • C++
  • Java
  • JavaScript
  • Python
cpp
class Solution {
public:
    int maximumScore(vector<int>& elements) {
        // Calculating potential starts
        int totalStates = 1 << elements.size();
        int allPickedState = totalStates - 1;

        // dp array to store results for states
        vector<int> scores(totalStates);

        // Loop through all the states
        for (int currState = allPickedState; currState >= 0; --currState) {
            // If all elements are used, score is zero
            if (currState == allPickedState) {
                scores[currState] = 0;
                continue;
            }

            // Count taken elements and formed pairs
            int takenCount = __builtin_popcount(currState);
            int formedPairs = takenCount / 2;
            // Invalid states with odd numbers taken
            if (takenCount % 2 != 0) {
                continue;
            }

            // Trying to form one more pair
            for (int i = 0; i < elements.size(); ++i) {
                for (int j = i + 1; j < elements.size(); ++j) {
                    // Check if elements i and j are not picked
                    if (((currState >> i) & 1) || ((currState >> j) & 1)) {
                        continue;
                    }
                    // Calculate new score if i and j are paired
                    int pairScore = (formedPairs + 1) * __gcd(elements[i], elements[j]);
                    int newState = currState | (1 << i) | (1 << j);
                    scores[currState] = max(scores[currState], pairScore + scores[newState]);
                }
            }
        }

        // Return the score starting with no elements picked
        return scores[0];
    }
};

The provided C++ solution addresses the problem of maximizing a score after performing certain operations on an array of integers. Here's a summary of how the solution works:

  • Initialization:

    • Calculate the total number of states using a bit representation, where every bit corresponds to whether an element in the array is used or not.
    • Create an array scores to keep track of the maximum score that can be achieved from any state.
  • Dynamic Programming Calculation:

    1. Iterate over all possible states of the elements from the state where all elements are picked to the state where no elements are picked.
    2. Skip the processing for states with an odd number of elements picked, as they cannot form pairs.
    3. For every pair of elements not yet picked, calculate a possible score from picking them, based on the greatest common divisor (gcd) of the pair, multiplied by the number of formed pairs incremented by one.
    4. Update the scores array if a new higher score is found for the current state.
  • Result Extraction:

    • Return the score found for the state where no elements are initially picked, which represents the maximum score achievable with optimal operations.

The solution leverages bit masking to efficiently handle combinations of elements, and dynamic programming to avoid redundant computations, achieving an optimized approach to solving the problem.

java
class Solution {
    public int maxPoints(int[] array) {
        int numberStates = 1 << array.length; 
        int completeState = numberStates - 1;

        int[] maxPoints = new int[numberStates];

        for (int bitmask = completeState; bitmask >= 0; bitmask--) {
            if (bitmask == completeState) {
                maxPoints[bitmask] = 0;
                continue;
            }

            int elementsChosen = Integer.bitCount(bitmask);
            int completedPairs = elementsChosen / 2;
            if (elementsChosen % 2 != 0) {
                continue;
            }

            for (int index1 = 0; index1 < array.length; index1++) {
                for (int index2 = index1 + 1; index2 < array.length; index2++) {
                    if (((bitmask >> index1) & 1) == 1 || ((bitmask >> index2) & 1) == 1) {
                        continue;
                    }
                    int currentPairScore = (completedPairs + 1) * greatestCommonDivisor(array[index1], array[index2]);
                    int newBitmask = bitmask | (1 << index1) | (1 << index2);
                    int scoreAfterPair = maxPoints[newBitmask];
                    maxPoints[bitmask] = Math.max(maxPoints[bitmask], currentPairScore + scoreAfterPair);
                }
            }
        }

        return maxPoints[0];
    }

    private int greatestCommonDivisor(int x, int y) {
        if (y == 0) {
            return x;
        }
        return greatestCommonDivisor(y, x % y);
    }
}

The Java program provided is designed to solve the problem of maximizing the score obtained through a series of operations on an integer array. Each operation involves selecting two distinct elements from the array, forming one pair, and calculating the score for that pair based on the product of the pair's position in the selection sequence and their greatest common divisor (GCD).

Here's how the solution works:

  • The solution utilizes a dynamic programming approach with a bitmask to represent states. Each bit in the bitmask can either be 0 or 1, representing whether a corresponding element in the array is included in the current set of operations.

  • The maxPoints array is used to store the maximum score achievable for each bitmask state, where each element's index corresponds to a specific bitmask.

  • The process starts by initializing the final state (completeState), which indicates that all elements in the array are paired. It then works backwards through every possible bitmask state.

  • During each iteration, the program checks if the number of elements included (as indicated by the number of set bits in the bitmask) allows forming a complete pair. If not, it skips to the next iteration.

  • For each valid state, the solution looks for two elements that have not been paired yet (as indicated by their corresponding bits in the bitmask being 0). It then calculates the potential score achieved by pairing these two elements and updates the maxPoints for the current bitmask if this new score is higher than the previously recorded one.

  • The score for each pair is calculated as the product of the sequence number of the operation by which this pair is processed (derived from the number of completed pairs + 1) and the GCD of the two chosen elements.

  • The method greatestCommonDivisor calculates the GCD of two numbers using recursion.

  • The program returns the maximum score from the base state (maxPoints[0]), which represents the state with no elements paired.

Overall, the program effectively enumerates and evaluates possible pairs from the array, carefully optimizing using bit manipulation and dynamic programming to ensure all valid combinations are considered to maximize the final score.

js
var calculateMaxScore = function(elements) {
    const totalStates = 1 << elements.length;
    const allPickedMask = totalStates - 1;

    const scoreMemo = new Array(totalStates).fill(0);

    for (let config = allPickedMask; config >= 0; config--) {
        if (config == allPickedMask) {
            scoreMemo[config] = 0;
            continue;
        }

        const countOnes = config.toString(2).split('1').length - 1;
        const alreadyFormedPairs = countOnes / 2;

        if (countOnes % 2) {
            continue;
        }

        for (let i = 0; i < elements.length; i++) {
            for (let j = i + 1; j < elements.length; j++) {
                if (((config >> i) & 1) == 1 || ((config >> j) & 1) == 1) {
                    continue;
                }
                const scoreFromPair = (alreadyFormedPairs + 1) * findGCD(elements[i], elements[j]);
                const newConfig = config | (1 << i) | (1 << j);
                const futureScore = scoreMemo[newConfig];
                scoreMemo[config] = Math.max(scoreMemo[config], scoreFromPair + futureScore);
            }
        }
    }

    return scoreMemo[0];
}

function findGCD(x, y) {
    if (y == 0) {
        return x;
    }
    return findGCD(y, x % y);
}

The provided solution tackles the problem of maximizing a score after N operations using JavaScript. The function calculateMaxScore receives an array called elements and calculates the maximum possible score by strategically forming and pairing elements based on certain criteria.

  • Define dynamic programming states using totalStates, representing all possible combinations of picking elements from elements.
  • Utilize allPickedMask as a bitmask to manage which elements have been paired.
  • Employ scoreMemo to store intermediate results for various states, thus avoiding redundant calculations.
  • Implement two nested loops to iterate through all potential pairs of elements. Each element's participation in pairs is tracked using bit manipulation. Conditions ensure that elements are only paired if neither is already paired in the current configuration.
  • Calculate the score for each valid pair using the utility function findGCD, which finds the greatest common divisor of two numbers, reflecting the score contribution of the pair.
  • Update scoreMemo to store the maximum score for each configuration using dynamic programming.
  • Return the maximum obtainable score from the initial state where no elements are paired (scoreMemo[0]).

This method ensures that the solution is both efficient and thorough, systematically exploring all possibilities while keeping the computation manageable via memoization and bit manipulation techniques.

python
class Solution:
    def maximumScore(self, numbers: List[int]) -> int:
        numStates = 1 << len(numbers)  # Calculate 2 raised to the power of the length of the input numbers.
        finalState = numStates - 1

        # 'scoreMemo' stores maximum score possible with unselected numbers represented by binary state.
        scoreMemo = [0] * numStates

        # Process each state from fully populated to empty.
        for mask in range(finalState, -1, -1):
            # End case where all numbers have been taken.
            if mask == finalState:
                scoreMemo[mask] = 0
                continue

            countOnes = bin(mask).count('1')
            numPairs = countOnes // 2
            
            # Only process states with even numbers of bits set (even selections).
            if countOnes % 2:
                continue

            # Creating pairs and checking all two-number combinations.
            for i in range(len(numbers)):
                for j in range(i + 1, len(numbers)):
                    # Ensure both numbers are not already selected.
                    if (mask >> i & 1) or (mask >> j & 1):
                        continue
                    pairScore = (numPairs + 1) * math.gcd(numbers[i], numbers[j])
                    newMask = mask | (1 << i) | (1 << j)
                    scoreFromKnown = scoreMemo[newMask]
                    scoreMemo[mask] = max(scoreMemo[mask], pairScore + scoreFromKnown)

        # Return the maximum score obtainable using all numbers.
        return scoreMemo[0]

This Python solution focuses on maximizing a score based on GCD (Greatest Common Divisor) calculations after N operations, employing a dynamic programming approach using bit masking. Here’s a concise breakdown of how the solution manages to achieve this:

  • Initialize states: The solution calculates all possible states using bit manipulation, with each state representing a combination of numbers selected so far.

  • Memoization setup: A list scoreMemo is maintained to store the maximum score obtainable for every state (each possible selection of numbers).

  • Iterative computation: The solution iterates backwards through all states, calculating the possible scores starting from states with no numbers selected. This reverse iteration ensures that by the time a state is processed, all states that can be reached from it by selecting more numbers have already been considered.

  • Handling pairs: For each state, the solution identifies possible pairs of numbers that can be added to the state. It calculates the potential score for each pair by computing its GCD, multiplied by a value that evolves with the number of pairs already formed. This new score is then potentially used to update the memoized scores.

  • Euclidean algorithm for GCD: The use of GCD in scoring ensures that the combination of numbers with higher mutual divisibility yields a higher score. This is beneficial in strategizing the number selection process.

  • Result extraction: Finally, the maximum score for using all numbers, which corresponds to the initial state where no numbers are selected (binary state 000...0), is returned from the scoreMemo array.

Optimizations include only considering states with an even number of selected items (to form pairs), and careful checks to ensure that each number is used optimally in forming pairs. This solution leverages bit manipulation to efficiently track and update states, enabling it to handle the combinatorial complexity inherent in the problem.

Comments

No comments yet.