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 0→i−1的最大和 s m o d 3 = = 0 s\mod3==0 smod3==0。
如果 k=1,需要找到 0 → i − 1 0\rightarrow i-1 0→i−1的最大和 s m o d 3 = = 2 s\mod3==2 smod3==2。
如果 k=2,需要找到 0 → i − 1 0\rightarrow i-1 0→i−1的最大和 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[i−1]的最大和s。
用$f[i][j] $表示 前 0 → i − 1 0\rightarrow i-1 0→i−1个元素可以得到的最大和 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[i−1][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[i−1][(j−x)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==((j−x)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[i−1][j],f[i−1][((j−x)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[i−1]元素进行处理。
代码
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