You are given nums
, an array of positive integers of size 2 * n
. You must perform n
operations on this array.
In the ith
operation (1-indexed), you will:
- Choose two elements,
x
andy
. - Receive a score of
i * gcd(x, y)
. - Remove
x
andy
fromnums
.
Return the maximum score you can receive after performing n
operations.
The function gcd(x, y)
is the greatest common divisor of x
and y
.
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
Related Topics:
Dynamic Programming, Backtracking, Recursion
Let dp[m]
be the maximum score we can get on subset m
where m
is a binary mask representation of a subset of array A
. (e.g. m = 0101
means that A[0]
and A[2]
are in the subset m
)
Given a subset m
, we can select two elements out of it as the last operation. Assume their indexes are i
and j
respectively, the score we get from the last operation is cnt / 2 * gcd(A[i], A[j])
where cnt
is the number of bit 1s in m
. For the rest of the elements, they form a subset represented by next = m & ~(1 << i) & ~(1 << j)
, whose maximum score is dp[next]
.
dp[m] = max( cnt / 2 * gcd(A[i], A[j]) + dp[next] )
where i
and j
are two different indexes of bit 1s in m
, and next = m & ~(1 << i) & ~(1 << j)
represents the subset m
excluding A[i]
and A[j]
.
We traverse the binary masks from 2
to 2^N - 1
so the outer for
loop will run O(2^N)
time.
In each round of for
loop, we take O(N^2)
time to traverse all the pairs of elements in the subset m
.
Thus, overall it takes O(2^N * N^2)
time.
And the space complexity is apparently O(2^N)
as well because of the dp
array.
// OJ: https://leetcode.com/problems/maximize-score-after-n-operations/
// Author: github.com/lzl124631x
// Time: O(2^N * N^2)
// Space: O(2^N)
class Solution {
public:
int maxScore(vector<int>& A) {
int N = A.size();
vector<int> dp(1 << N);
for (int m = 3; m < (1 << N); ++m) { // `m` is a binary mask representation of the elements in the subset
int cnt = __builtin_popcount(m);
if (cnt % 2) continue; // if there are even number of elements in this subset, skip
vector<int> b; // indexes of elements in this subset
for (int tmp = m, i = 0; tmp; ++i, tmp >>= 1) {
if (tmp & 1) b.push_back(i); // `m` has bit 1 at position `i`, which means that `A[i]` is in the current subset
}
for (int i = 0; i < b.size() ; ++i) {
for (int j = i + 1; j < b.size(); ++j) { // use different pairs as the last operation on this subset
int next = m & ~(1 << b[i]) & ~(1 << b[j]); // `next` represents the subset after removing `A[b[i]]` and `A[b[j]]` from subset `m`
dp[m] = max(dp[m], cnt / 2 * gcd(A[b[i]], A[b[j]]) + dp[next]); // cnt / 2 * gcd(A[b[i]], A[b[j]]) is the score we get in the last operation. dp[next] is the maximum score we can get on the subset `next.
}
}
}
return dp.back();
}
};