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

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

    • 状态转移

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

      取最大值返回
      d f s ( i , j ) = m a x ( d f s ( i − 1 , j ) , d f s ( i − 1 , ( j + x ) % 3 + n u m s [ 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]))

    • 递归边界

      • d f s ( − 1 , 0 ) = 0 dfs(-1,0)=0 dfs(1,0)=0
      • d f s ( − 1 , 1 ) = − ∞ dfs(-1,1)=- \infin dfs(1,1)=
      • d f s ( − 1 , 2 ) = − ∞ dfs(-1,2)=- \infin dfs(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)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

你可能感兴趣的:(每日一题,贪心,算法,leetcode)