不回答这样一个问题似乎很可惜,因为它并不难解决 - 让我们使用你的最后一个例子:
- A 有 53 美元
- B 有 95 美元
- C 有 24 美元
- 人 D 有 98 美元
- E 人有 30 美元
为了简单起见,我将使用数组。
const totals = [53, 95, 24, 98, 30]
let sum, target, moveCounter = 0
if (totals.length) {
sum = totals.reduce(function(a, b) { return (a + b) })
target = sum / totals.length
}
目标只是总数的平均值,在示例中为 60 美元。
let needs = totals.map(function(a){ return target - a })
// needs = [7, -35, 36, -38, 30]
现在您知道每个needs 是什么。
[0] 需要得到 7 美元,
[1] 需要降低 35 美元,
[2] 需要得到 36 美元,
等等
数组是零和:(7) + (-35) + (36) + (-38) + (30) = 0
您的问题是如何在尽可能少的移动中使每个元素都为 0。
您可以检查是否有任何元素相互抵消,例如 [20, -20]。一个有效的检查方法是创建一个 Set。
let unmatched = new Set([])
let matches = []
needs.forEach((need) => {
if (0 !== need) {
let value = Math.abs(need)
if (unmatched.has(value)) {
matches.concat([need, -need])
unmatched.delete(value)
} else {
unmatched.add(value)
}
}
})
if (matches.length) {
matches.forEach((matchElement) => {
let index = needs.findIndex((needElement) => {
return matchElement === needElement
})
if (index) {
needs[index] = needs[index] - matchElement
moveCounter = moveCounter + 1
})
}
此时所有简单的动作都已完成,您可以对数组进行排序并暴力破解其余的。排序让事情变得更容易一些。
var sortedNeeds = needs.sort(function(a, b){ return a < b ? -1 : 1 })
// [-38, -35, 7, 30, 36]
数组是零和,因此您可以将其分成两部分 left 和 right。把它想象成一个平衡方程的左右两边。
const zeroIndex = sortedNeeds.findIndex((value) => { return 0 === value })
// zeroIndex : -1
const positiveIndex = sortedNeeds.findIndex((value) => { return value > 0 })
// positiveIndex : 2
const leftLimit = (zeroIndex > -1) ? zeroIndex : positiveIndex
// leftLimit : 2
let left = sortedNeeds.slice(0, leftLimit)
// left: [-38, -35]
let right = sortedNeeds.slice(positiveIndex)
// right: [7, 30, 36]
下一段是魔法:
moveCounter = moveCounter + (left.length - 1) + right.length
// 4 = 0 + 1 + 3
如何计算并不重要,因为零和关系是关键。
- D 给 B 38 美元
- B 给 A 7 美元
- B 给 C 36 美元
- B 给 E 30 美元