Leetcode 1262:可被三整除的最大和(超详细的解法!!!)

给你一个整数数组 nums,请你找出并返回能被三整除的元素最大和。

示例 1:

输入:nums = [3,6,5,1,8]
输出:18
解释:选出数字 3, 6, 1 和 8,它们的和是 18(可被 3 整除的最大和)。

示例 2:

输入:nums = [4]
输出:0
解释:4 不能被 3 整除,所以无法选出数字,返回 0。

示例 3:

输入:nums = [1,2,3,4,4]
输出:12
解释:选出数字 1, 3, 4 以及 4,它们的和是 12(可被 3 整除的最大和)。

提示:

  • 1 <= nums.length <= 4 * 10^4
  • 1 <= nums[i] <= 10^4

解题思路

可以这样思考,遍历整个数组然后将数分成三个部分,%3==0%3==1%3==2

然后整个数组的和sum_all3等于0,那么这就是最大值了。

如果等于1,那么我们需要从%3==1的数中选一个最小的数t1,然后再从%3==2中选两个最小的数并计算和t2,比较t1t2选择最小的,然后sum_all减去即可。

如果等于2,那么我们需要从%3==2的数中选一个最小的数t1,然后再从%3==1中选两个最小的数并计算和t2,比较t1t2选择最小的,然后sum_all减去即可。

class Solution:
    def maxSumDivThree(self, nums: List[int]) -> int:
        b0, b1, b2 = [], [], []
        sum_all = 0
        for i in nums:
            if i % 3 == 0:
                b0.append(i)
            elif i % 3 == 1:
                b1.append(i)
            else:
                b2.append(i)
            sum_all += i
        if sum_all % 3 == 0:
            return sum_all
        elif sum_all % 3 == 1:
            t1, t2 = 0, 0
            if len(b1): t1 = sum_all - min(b1)
            if len(b2) > 1: t2 = sum_all - sum(sorted(b2)[:2])
            return max(t1, t2)
        else:
            t1, t2 = 0, 0
            if len(b2): t1 = sum_all - min(b2)
            if len(b1) > 1:t2 = sum_all - sum(sorted(b1)[:2])
            return max(t1, t2)

使用动态规划更加简洁。定义 f ( i , j ) f(i,j) f(i,j)表示到nums[i]为止满足sum%3==j的最大sum是多少,那么

  • nums[i]%3==0 f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , j ) + n u m s [ i − 1 ] ) f(i,j)=max(f(i-1,j),f(i-1,j)+nums[i-1]) f(i,j)=max(f(i1,j),f(i1,j)+nums[i1])
  • nums[i]%3==1 f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , ( j + 2 ) % 3 ) + n u m s [ i − 1 ] ) f(i,j)=max(f(i-1,j), f(i-1,(j+2)\%3)+nums[i-1]) f(i,j)=max(f(i1,j),f(i1,(j+2)%3)+nums[i1])
  • nums[i]%3==2 f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , ( j + 1 ) % 3 ) + n u m s [ i − 1 ] ) f(i,j)=max(f(i-1,j),f(i-1,(j+1)\%3)+nums[i-1]) f(i,j)=max(f(i1,j),f(i1,(j+1)%3)+nums[i1])

所以可以推出

  • f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , ( j + 3 − n u m s [ i − 1 ] % 3 ) % 3 ) + n u m s [ i − 1 ] ) f(i,j)=max(f(i-1,j),f(i-1,(j+3-nums[i-1]\%3)\%3)+nums[i-1]) f(i,j)=max(f(i1,j),f(i1,(j+3nums[i1]%3)%3)+nums[i1])

还需要注意的一点是,当且仅当上面式子中 f ( i − 1 , ( j + 3 − n u m s [ i − 1 ] % 3 ) % 3 ) + n u m s [ i − 1 ] % = = j ​ f(i-1,(j+3-nums[i-1]\%3)\%3)+nums[i-1]\%==j​ f(i1,(j+3nums[i1]%3)%3)+nums[i1]%==j的时候式子才成立。

class Solution:
    def maxSumDivThree(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [[0]*3 for _ in range(n + 1)]
        for i in range(1, n + 1):
            for j in range(3):
                a = dp[i - 1][(j + 3 - nums[i-1]%3) % 3] + nums[i-1]
                if a % 3 == j:
                    dp[i][j] = max(dp[i-1][j], a)
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[-1][0]

还可以更加简洁。定义 f ( i ) f(i) f(i)表示当前满足sum%3==i的最大sum是多少,那么

  • f ( ( f ( i ) + a ) % 3 ) = m a x ( f ( ( f ( i ) + a ) % 3 ) , f ( i ) + a ) f((f(i) + a)\%3)=max(f((f(i)+a)\%3), f(i) + a) f((f(i)+a)%3)=max(f((f(i)+a)%3),f(i)+a)

其中a表示nums中的每个数。

class Solution:
    def maxSumDivThree(self, nums: List[int]) -> int:
        dp = [0, 0, 0]
        for a in nums:
            for i in dp[:]:
                dp[(i + a) % 3] = max(dp[(i + a) % 3], i + a)
        return dp[0]

reference:

https://leetcode.com/problems/greatest-sum-divisible-by-three/discuss/431077/Python-One-Pass-O(1)-space

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(leetcode解题指南,Problems)