【算法】Greatest Sum Divisible by Three 可被三整除的最大和- 动态规划

文章介绍了如何使用动态规划解决找到给定数组中能被三整除的最大和的问题。通过分析,提出了利用01背包思想,根据元素对3的余数将问题分解,并建立状态转移方程,最后给出两种不同空间复杂度的解决方案,包括使用滚动数组优化空间需求。
摘要由CSDN通过智能技术生成

Greatest Sum Divisible by Three 可被三整除的最大和-动态规划

问题描述:

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

nums.length 范围[1,40000] , n u m s [ i ] nums[i] nums[i] 范围[1,10000]

分析

贪心策略的减法,虽然时间复杂度和操作性简单,但是适用性并不广泛。
以01背包的思路来思考这个问题,对于 a [ i ] a[i] a[i]来说,如果选择,而 a [ i ] m o d    3 = = k a[i]\mod3==k a[i]mod3==k,即 k = 0 , 1 , 2 k=0,1,2 k=0,1,2.
如果 k=0,需要找到 0 → i − 1 0\rightarrow i-1 0i1的最大和 s m o d    3 = = 0 s\mod3==0 smod3==0
如果 k=1,需要找到 0 → i − 1 0\rightarrow i-1 0i1的最大和 s m o d    3 = = 2 s\mod3==2 smod3==2
如果 k=2,需要找到 0 → i − 1 0\rightarrow i-1 0i1的最大和 s m o d    3 = = 1 s\mod3==1 smod3==1
如果不选
当前的位置就对应 a [ 0 ] → a [ i − 1 ] a[0]\rightarrow a[i-1] a[0]a[i1]的最大和s。
用$f[i][j] $表示 前 0 → i − 1 0\rightarrow i-1 0i1个元素可以得到的最大和 s s s,并且 s m o d    3 = = j s\mod3==j smod3==j .
令a[i] = x
不选a[i], f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j]
选a[i], f [ i ] [ j ] = f [ i − 1 ] [ ( j − x ) m o d    3 ] f[i][j]= f[i-1][(j-x)\mod3] f[i][j]=f[i1][(jx)mod3]

( s + x ) m o d    3 = = j 转换 s m o d    3 = = ( ( j − x ) m o d    3 + 3 ) m o d    3 (s+x)\mod 3==j \\ 转换\\ s\mod 3 == ((j-x)\mod 3 + 3)\mod 3 \\ (s+x)mod3==j转换smod3==((jx)mod3+3)mod3
所以状态转移方程就长这个样子。

f [ i ] [ j ] = max ⁡ ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ ( ( j − x ) m o d    3 + 3 ) m o d    3 ] + a [ i ] ) f[i][j] = \max (f[i-1][j],f[i-1][((j-x)\mod3+3)\mod 3] + a[i] ) f[i][j]=max(f[i1][j],f[i1][((jx)mod3+3)mod3]+a[i])
如果这个方程看不明白,也可以换个角度。
f [ i ] [ j ] f[i][j] f[i][j]定义不变
f [ i + 1 ] [ j ] = f [ i ] [ j ] f[i+1][j] = f[i][j] f[i+1][j]=f[i][j]
f [ i + 1 ] [ j ] = f [ i ] [ ( j m o d    3 + x m o d    3 ) m o d    3 ] f[i+1][j] = f[i][(j\mod3+x\mod3) \mod 3] f[i+1][j]=f[i][(jmod3+xmod3)mod3]
f [ i + 1 ] [ j ] = max ⁡ ( f [ i ] [ j ] , f [ i ] [ ( j + a [ i ] ) m o d    3 ] + a [ i ] ) f[i+1][j] = \max (f[i][j],f[i][(j+ a[i])\mod3] + a[i] ) f[i+1][j]=max(f[i][j],f[i][(j+a[i])mod3]+a[i])
为了方便计算,对原始下标做一次偏移,初始化边界 f [ 0 ] [ 0 ] = 0 , f [ 0 ] [ 1 ] = I N T M I N , f [ 0 ] [ 2 ] = I N T M I N f[0][0]=0,f[0][1]=INTMIN,f[0][2]=INTMIN f[0][0]=0,f[0][1]=INTMIN,f[0][2]=INTMIN。即 f [ i ] f[i] f[i]表示对原数组的 a [ i − 1 ] a[i-1] a[i1]元素进行处理。

代码

class Solution {
    public int maxSumDivThree(int[] nums) { 
		int n = nums.length;
        int[][] f = new int[n + 1][3];
        f[0][1] = f[0][2] = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++){ 
            for (int j = 0; j < 3; j++){ 
                f[i][j] = f[i-1][j];
                int pre = ((j-nums[i-1])%3+3)%3;
                f[i][j] = Math.max(f[i][j],f[i-1][pre] + nums[i-1]);
            }
        }         
        return f[n][0]; 
    }
}

时间复杂度O(Nk)

空间复杂度O(Nk)

public int maxSumDivThree(int[] nums) { 
		int n = nums.length;
        int[][] 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++){  
                int pre = (j+nums[i])%3;
                f[i+1][j] = Math.max(f[i][j],f[i][pre] + nums[i]);
            }
        }         
        return f[n][0]; 
    }

时间复杂度O(Nk)

空间复杂度O(Nk)

因为状态转移方程的特点,空间可以进一步压缩,使用滚动数组可以把空间压缩到 O ( k ) O(k) O(k)

Tag

Array Dynamic Programming

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric.Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值