问题
小美是美团仓库的管理员,她会根据单据的要求按顺序取出仓库中的货物,每取出一件货物后会把剩余货物重新堆放,使得自己方便查找。已知货物入库的时候是按顺序堆放在一起的。如果小美取出其中一件货物,则会把货物所在的一堆物品以取出的货物为界分成两堆,这样可以保证货物局部的顺序不变。
已知货物最初是按 1~n 的顺序堆放的,每件货物的重量为 w[i] ,小美会根据单据依次不放回的取出货物。请问根据上述操作,小美每取出一件货物之后,重量和最大的一堆货物重量是多少?
分析
首先有一个疑问,这样做真的能方便货物的查找吗?
我们换一种说法来帮助理解此题目。有一个整数数组,给出一个下标将数组分成2个子数组,分别求出各子数组元素和,找到元素和最大的子数组,重复上述操作,如下图:
- 下标4,子数组最大元素和sum=9
- 下标2,子数组最大元素和sum=4
- 下标3,子数组最大元素和sum=3
- 下标1,子数组最大元素和sum=1
- 下标0,子数组最大元素和sum=0
代码及解析
public void manage(int[] weights, int[] nums) {
int maxSum = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
// 判断是否需要计算"最大货堆重量和",减少计算量提升运行效率
boolean needSum = needSum(weights, nums[i] - 1, maxSum);
// 重量设置为0表示取出货物
weights[nums[i] - 1] = 0;
// 如果待取出货物的在"最大货堆"内,"最大货堆"将被分割,则需要重新计算"最大货堆重量和";
// 否则"最大货堆重量和"不变,不需要计算
maxSum = needSum ? maxSum(weights) : maxSum;
System.out.println(maxSum);
}
}
/**
* 是否需要计算"最大货堆重量和"
*
* @param weights
* @param index
* @param maxSum
* @return
*/
private boolean needSum(int[] weights, int index, int maxSum) {
// 初始需要计算"最大货堆重量和"
if (maxSum == Integer.MIN_VALUE) return true;
int left = index - 1, right = index + 1, currSum = weights[index];
// 计算待取出货物的"货堆的重量和"currSum
while (left >= 0 || right < weights.length) {
if (left >= 0) {
// 到了"货堆"的左边界,停止向左计算
if (weights[left] == 0) left = -1;
else currSum += weights[left--];
}
if (right < weights.length) {
// 到了"货堆"的右边界,停止向右计算
if (weights[right] == 0) right = weights.length;
else currSum += weights[right++];
}
}
// 如果待取出货物的在"最大货堆"内,"最大货堆"将被分割,则需要重新计算"最大货堆重量和"
return currSum == maxSum;
}
/**
* 计算"最大货堆重量和"
*
* @param weights
* @return
*/
private int maxSum(int[] weights) {
int maxSum = 0, temp = 0;
for (int j = 0; j < weights.length; j++) {
// weights[j]==0表示到达了"货堆"边界,需要计算新"货堆重量和"
temp = weights[j] == 0 ? 0 : temp + weights[j];
maxSum = Math.max(maxSum, temp);
}
return maxSum;
}