网易笔试 被6整除的最大子项和 JS
最近一次的网易前端笔试里有一题,题目大意是这样的:
求数组中最大的子项和,使得该子项和能被6整除,如果没有则返回-1
题目分析
要求数组能被6整除的最大子项和,相当于计算数组总和后对6取余得到总和余数,然后数组中对6取余恰好为总和余数的一项或多项组合去掉。
如果数组其中一项的余数恰好为总和余数,那直接把这一项剔除即可。
例如,数组[5,7,9]
,总和为
5
+
7
+
9
=
21
5+7+9=21
5+7+9=21
21对6取余后得到余数为3,那么找到数组中对6取余为3的那一项剔除即可。对数组每一项对6取余得到
数组元素 | 5 | 7 | 9 |
---|---|---|---|
对6取余结果 | 5 | 1 | 3 |
因此只要把9剔除,剩下的就是6的倍数。
但假如所有项的余数都恰好不是总和余数,则需要将这些元素进行组合,尝试产生新的余数,直至找到总和余数。
例如,数组[2,3,4,8]
,总和为
2
+
3
+
4
+
8
=
17
2+3+4+8=17
2+3+4+8=17
17对6取余后得到余数为5,对数组每一项对6取余得到
数组元素 | 2 | 3 | 4 | 8 |
---|---|---|---|---|
对6取余结果 | 2 | 3 | 4 | 2 |
发现数组中并没有直接使得余数为5的项,此时单独剔除一项已经无法满足要求,但如果是多项组合,能否得到想要的余数呢?
对已有余数的项进行组合,尝试得到新余数。
数组元素组合 | 2+3=5 | 2+4=6 | 3+4=7 |
---|---|---|---|
对6取余结果 | 5 | 0 | 1 |
此时可以得到总和余数5,因此只需把2和3两个剔除即可得到结果。
注意:如果数组中多项取得相同余数,那么只需取最小项进行组合,因为题目要求是求最大的子项和,有重复的选项选取最小的剔除即可。
那么,怎么找数组元素的组合,使得组合的余数为想要的结果呢?
从最小的元素遍历查找,并用一个余数数组来存放已经能获取到的余数的对应值,数组下标为余数,内容为算得该余数的元素。
遍历每个元素时,用当前元素与已有的余数进行组合,如果产生新的余数,则放到对应的位置,如果位置上有值了,则说明还有更小的方案能得到相应的余数,就不用再管。
如[2,3,4,8]
,遍历时,首先得到2这一项,因为余数数组是空的,则直接放入即可
余数 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
对应元素 | null | 2 | null | null | null |
到3这一项时,将3与余数数组中已有的2的一项组合,产生新的余数5,放入数组;3自身也是一个新的余数,同样放入数组
余数 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
对应元素 | null | 2 | 3 | null | 2+3 |
此时已经得到结果项。如果还未得到结果,则继续遍历。所有元素组合都得不到想要的结果时,则返回-1。
步骤
- 遍历整个数组,计算总和和余数,并把能被6整除的元素剔除
- 对数组进行排序
- 遍历数组,计算每项对6取余的结果,以及与其他项组合后对6取余的结果
- 用余数数组存放已有的余数结果,用于后续计算
- 重复3,4步骤,直至找到想要的余数结果
JS实现
function calc6(arr) {
let sum = 0, i = 0
// 计算总和,并剔除被6整除的项
while (i < arr.length) {
sum += arr[i]
if (arr[i] % 6 == 0) {
arr.splice(i, 1)
} else {
i++
}
}
// 计算和对6的余数
let sumRemain = sum % 6
if (sumRemain == 0) return sum
// 暂存可以能得到对应余数的最小结果
const remainArray = Array(6)
// 对arr排序,从最小开始寻找各个余数
arr.sort((a, b) => a - b)
i = -1
while (++i < arr.length) {
// 计算当前项的余数
let remain = arr[i] % 6
if (remain == sumRemain) return sum - arr[i]
// 存放当前项能组成的各种结果
let temp = [arr[i]]
// 与之前的余数结果进行组合
for (let i in remainArray) {
if (remainArray[i]) {
// 如果该项存在,则和当前项组合,获得新的组合结果
temp.push(remainArray[i] + arr[i])
}
}
// 遍历当前项组成的所有结果
for (let num of temp) {
// 计算每项的余数
let newRemain = num % 6
if (newRemain == sumRemain) return sum - num
// 如果不存在,则说明组合产生了新的余数,并放到对应位置
if (!remainArray[newRemain]) remainArray[newRemain] = num
}
}
return -1
}