class Solution:
def maxSumDivThree(self, nums):
dp = [0, float('-inf'), float('-inf')]
for num in nums:
temp = dp[:]
for i in range(3):
new_mod = (i + num) % 3
temp[new_mod] = max(temp[new_mod], dp[i] + num)
dp = temp
return dp[0]
class Solution {
public:
int maxSumDivThree(vector<int>& nums) {
vector<int> dp = {0, INT_MIN, INT_MIN};
for (int num : nums) {
vector<int> temp = dp;
for (int i = 0; i < 3; ++i) {
int new_mod = (i + num) % 3;
temp[new_mod] = max(temp[new_mod], dp[i] + num);
}
dp = temp;
}
return dp[0];
}
};
class Solution {
public int maxSumDivThree(int[] nums) {
int[] dp = {0, Integer.MIN_VALUE, Integer.MIN_VALUE};
for (int num : nums) {
int[] temp = dp.clone();
for (int i = 0; i < 3; ++i) {
int newMod = (i + num) % 3;
temp[newMod] = Math.max(temp[newMod], dp[i] + num);
}
dp = temp;
}
return dp[0];
}
}
var maxSumDivThree = function(nums) {
let dp = [0, -Infinity, -Infinity];
for (let num of nums) {
let temp = dp.slice();
for (let i = 0; i < 3; ++i) {
let newMod = (i + num) % 3;
temp[newMod] = Math.max(temp[newMod], dp[i] + num);
}
dp = temp;
}
return dp[0];
};
You are given an array of integers called nums
. Your task is to find the greatest possible sum of some (possibly all or none) elements of nums
such that the sum is divisible by 3.
You may choose any subset of elements from nums
, but you cannot reuse elements. The answer should be the maximum sum that is divisible by 3. If no such sum exists, return 0.
Key Constraints:
nums
can be used at most once.The first idea is to try all possible subsets and check which ones sum to a number divisible by 3, keeping track of the largest such sum. However, this "brute-force" approach is not practical for large arrays, since the number of subsets grows exponentially (2n).
To optimize, we notice that the only thing that matters about the sum is its remainder when divided by 3. So, instead of tracking every possible sum, we can track the best sums for each possible remainder (0, 1, or 2). This is a classic dynamic programming (DP) scenario: at each step, we update our best possible sum for each remainder as we process each number.
The main insight is that for each number, we can try adding it to each current "best sum" for each remainder, and see if that leads to a better sum for the new remainder.
dp[0]
, dp[1]
, dp[2]
. Each entry will track the maximum sum we can achieve so far with a sum whose remainder modulo 3 is 0, 1, or 2, respectively. Initially, dp[0]=0
(using no elements), and dp[1]=dp[2]=-∞
(impossible to get these remainders yet).
nums
, make a copy of the current DP array (since we'll update all three entries for this number).
i
from 0 to 2), try adding the current number to the sum for that remainder. The new sum's remainder will be (i + num) % 3
. Update the DP entry for that new remainder if this sum is better.
dp[0]
contains the largest sum divisible by 3.
This approach works because at each step, we only care about the best possible sum for each possible remainder, and we always keep these up to date.
Let's use the input nums = [3, 6, 5, 1, 8]
.
dp = [0, -∞, -∞]
The answer is 22
, the largest sum divisible by 3.
Brute-force approach: Would consider all 2n subsets, which is infeasible for large n.
Optimized DP approach:
nums
. For each number, we do a constant amount of work (3 updates).This makes the solution very efficient, even for large input arrays.
By recognizing that only the remainder modulo 3 matters, we avoid brute-force and use a compact dynamic programming approach. We keep track of the best possible sum for each remainder, updating as we process each number. This results in an elegant, efficient solution that runs in linear time and constant space, suitable for large input sizes.