超级洗衣机问题
作者:Grey
原文地址:
主要思路
有两个显而易见的结论:
假设所有衣服的数量之和是sum
,如果sum % N != 0
,则无论如何都无法做到让所有洗衣机平分衣服。
假设洗衣机的数量是size
,那么每个洗衣机需要分担avg = sum / size
这么多件衣服。
假设我们在i
位置,i
左侧有i-1
个位置,这i
个位置上应该要有i*avg
件衣服,假设现在i
位置之前的累加和是leftSum
, 用
leftRest = leftSum - i * avg
得到的结果就是i
左侧还差多少件衣服(如果是负数,就是富余多少件衣服)。
同理,i
右侧有size - i - 1
个位置,这size - i - 1
个位置理论上应该有avg * (size - i - 1)
件衣服。用
rightRest = sum - leftSum - avg*(size - i - 1)
得到的结果就是i
右侧还差多少件衣服(如果是负数,就是富余多少件衣服)。
如果
leftRest < 0 && rightRest < 0
说明i
位置两侧都不够衣服,要让i
位置同时给左侧和右侧都贡献衣服,因为可以不可同时贡献,所以i
贡献的衣服是
Math.abs(leftRest) + Math.abs(rightRest)
举例说明:
[3,6,3]
这个数组,对于中间位置的6来说,要向左侧贡献1件衣服,要向右侧贡献1件衣服。
除了这种情况,要么左侧衣服多了,要不右侧衣服多了,要不两侧衣服都多了,这个时候,需要两侧向i
位置贡献一部分衣服,因为左右两侧可以同时向i
位置贡献,所以贡献的衣服数量取决于是两侧多的数量,即:
Math.max(Math.abs(leftRest),Math.abs(rightRest))
举例说明:
[5,1,6]
这个数组,对于中间位置的1来说,左侧需要给它贡献1件衣服,右侧需要给它贡献2件衣服,而且根据题目条件,这种情况是可以同时贡献衣服的,所以只需要两轮即可。
自此,所有情况讨论完毕,完整代码如下:
public static int findMinMoves(int[] arr) {
if (null == arr || 0 == arr.length) {
return 0;
}
int sum = 0;
int size = arr.length;
for (int item : arr) {
sum += item;
}
if (sum % size != 0) {
return -1;
}
int avg = sum / size;
int leftSum = 0;
int ans = 0;
for (int i = 0; i < size; i++) {
// 左侧还差多少
int leftRest = leftSum - i * avg;
// 右侧还差多少
int rightRest = (sum - leftSum - arr[i]) - (size - i - 1) * avg;
if (leftRest < 0 && rightRest < 0) {
// 左侧右侧都差一些才到平均值
// 此时就需要中间位置向左边和右边都丢一些衣服
ans = Math.max(ans, Math.abs(leftRest) + Math.abs(rightRest));
} else {
// 左侧多,右侧少,多的通过中间丢一些到左侧
// 右侧少,左侧多,多的通过中间丢一些到右侧
// 左右侧都多,则可以**同时**向中间丢衣服
ans = Math.max(ans, Math.max(Math.abs(leftRest), Math.abs(rightRest)));
}
leftSum += arr[i];
}
return ans;
}
更多内容见:算法和数据结构笔记