【每日一题Day242】LC1262可被三整除的最大和 | 贪心 dp

文章介绍了如何解决寻找数组中能被三整除的最大和的问题。两种主要思路是贪心算法和动态规划。贪心策略是维护每个余数对应的最大和,动态规划则是通过递归或一维数组f来跟踪不同余数状态下的最大和。两种方法都以O(n)的时间复杂度和O(1)的空间复杂度实现。

可被三整除的最大和【LC1262】

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

  • 思路

    题目要求求出能被3整除的最大和,那么我们可以记录每种余数对应的最大和【贪心,相同余数的情况下取最大值】,那么答案即为val[0]。枚举每一个数,将其与目前的最大和相加得到三个可能的答案,然后存入数组中记录最大值

  • 实现

    class Solution {
        public int maxSumDivThree(int[] nums) {
            // 哈希表
            int[] val = new int[3];// 记录余数对应的最大和->那么答案即为val[0]
            for (int num : nums){  
                int a = val[0] + num;
                int b = val[1] + num;
                int c = val[2] + num;
                val[a % 3] = Math.max(a, val[a % 3]);
                val[b % 3] = Math.max(b, val[b % 3]);
                val[c % 3] = Math.max(c, val[c % 3]);
            }
            return val[0];
        }
    }
    
    • 复杂度分析

      • 时间复杂度:O(n)O(n)O(n)
      • 空间复杂度:O(1)O(1)O(1)

dp:选或不选

  • 思路:选或不选

    • 子问题:

      • x=nums[n−1]x=nums[n-1]x=nums[n1],那么问题变为在nums[:n−2]nums[:n-2]nums[:n2]中选择最大的x数之和aaa使其与nums[n−1]nums[n-1]nums[n1]之和能够整除3【aaaxxx的对三取余为0,那么(a+x)%3==0(a+x)\%3==0(a+x)%3==0->a%3=(0−x)%3a\%3=(0-x)\%3a%3=(0x)%3
      • 不选nums[n−1]nums[n-1]nums[n1],那么问题变为在nums[:n−2]nums[:n-2]nums[:n2]中选择最大的x数之和能够整除3
      • 这是一个和原问题相似的子问题,因此我们可以用递归/dp解决。
      • 递归的过程中有两个变量:元素范围以及和的余数值,因此在记忆化需要用二维memo记录这两个变量固定时对应的值
    • 递归函数定义:定义dfs(i,j)dfs(i,j)dfs(ij)为从nums[0]nums[0]nums[0]nums[i]nums[i]nums[i]中选数,**所选数字之和对3取模为jjj**时的最大和,那么dfs(n−1,0)dfs(n-1,0)dfs(n10)即为答案。

    • 状态转移

      • xxx,问题变为从nums[0]nums[0]nums[0]nums[i−1]nums[i-1]nums[i1]中选数,所选数字之和对3取模为jjj时的最大和
      • 不选xxx,问题变为从nums[0]nums[0]nums[0]nums[i−1]nums[i-1]nums[i1]中选数,所选数字之和对3取模为(j+x)%3(j+x)\% 3(j+x)%3时的最大和

      取最大值返回
      dfs(i,j)=max(dfs(i−1,j),dfs(i−1,(j+x)%3+nums[i])) dfs(i,j)=max(dfs(i-1,j),dfs(i-1,(j+x)\%3 + nums[i])) dfs(i,j)=max(dfs(i1,j),dfs(i1,(j+x)%3+nums[i]))

    • 递归边界

      • dfs(−1,0)=0dfs(-1,0)=0dfs(1,0)=0
      • dfs(−1,1)=−∞dfs(-1,1)=- \infindfs(1,1)=
      • dfs(−1,2)=−∞dfs(-1,2)=- \infindfs(1,2)=
  • 实现

    class Solution {
        public int maxSumDivThree(int[] nums) {
            int n = nums.length;
            var memo = new int[n][3];
            for (int i = 0; i < n; i++)
                Arrays.fill(memo[i], -1); // -1 表示没有计算过
            return dfs(memo, nums, n - 1, 0);
        }
    
        private int dfs(int[][] memo, int[] nums, int i, int j) {
            if (i < 0) return j == 0 ? 0 : Integer.MIN_VALUE;
            if (memo[i][j] != -1) return memo[i][j]; // 之前计算过
            return memo[i][j] = Math.max(dfs(memo, nums, i - 1, j),
                    dfs(memo, nums, i - 1, (j + nums[i]) % 3) + nums[i]);
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/greatest-sum-divisible-by-three/solutions/2313700/liang-chong-suan-fa-tan-xin-dong-tai-gui-tsll/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
  • 递推

    扩展一维找到边界

    class Solution {
        public int maxSumDivThree(int[] nums) {
            int n = nums.length;
            var f = new int[n + 1][3];
            f[0][1] = f[0][2] = Integer.MIN_VALUE;
            for (int i = 0; i < n; i++)
                for (int j = 0; j < 3; j++)
                    f[i + 1][j] = Math.max(f[i][j], f[i][(j + nums[i]) % 3] + nums[i]);
            return f[n][0];
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/greatest-sum-divisible-by-three/solutions/2313700/liang-chong-suan-fa-tan-xin-dong-tai-gui-tsll/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值