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