【发布时间】:2017-07-26 13:21:21
【问题描述】:
我正在练习解决 Codility 问题,但无法回答其中一个问题。我在互联网上找到了答案,但我不明白这个算法是如何工作的。有人可以逐步指导我吗? 问题来了:
/*
You are given integers K, M and a non-empty zero-indexed array A consisting of N integers.
Every element of the array is not greater than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y]. The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15;
[2], [1, 5, 1, 2], [2, 2] with a large sum of 9;
[2, 1, 5], [], [1, 2, 2, 2] with a large sum of 8;
[2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the minimal large sum.
Write a function:
class Solution { public int solution(int K, int M, int[] A); }
that, given integers K, M and a non-empty zero-indexed array A consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
the function should return 6, as explained above. Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
Complexity:
expected worst-case time complexity is O(N*log(N+M));
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
*/
这是我在我的 cmets 中找到的关于我不明白的部分的解决方案:
public static int solution(int K, int M, int[] A) {
int lower = max(A); // why lower is max?
int upper = sum(A); // why upper is sum?
while (true) {
int mid = (lower + upper) / 2;
int blocks = calculateBlockCount(A, mid); // don't I have specified number of blocks? What blocks do? Don't get that.
if (blocks < K) {
upper = mid - 1;
} else if (blocks > K) {
lower = mid + 1;
} else {
return upper;
}
}
}
private static int calculateBlockCount(int[] array, int maxSum) {
int count = 0;
int sum = array[0];
for (int i = 1; i < array.length; i++) {
if (sum + array[i] > maxSum) {
count++;
sum = array[i];
} else {
sum += array[i];
}
}
return count;
}
// returns sum of all elements in an array
private static int sum(int[] input) {
int sum = 0;
for (int n : input) {
sum += n;
}
return sum;
}
// returns max value in an array
private static int max(int[] input) {
int max = -1;
for (int n : input) {
if (n > max) {
max = n;
}
}
return max;
}
【问题讨论】:
-
"Lower" 是您可以从该数组中获得的最低大和,这是最大的数组元素,因为无论您做什么,都无法获得低于此的大和。如果您只是将所有元素相加并将所有其他块留空,那么您会得到“上”。这是您可以从该数组中获得的最大总和,这也是您将尝试在算法中最小化的总和。
-
哦,我现在明白了。块呢?你能解释一下吗?比如 int 块和计算它的方法背后的逻辑是什么?
-
Umm.. 首先你以目标总和,一些“相当不错的价值”作为开始,在你的情况下是
(lower + upper)/2,然后尝试看看你是否能够收集K子数组,其中每个数组的总和等于或低于您的目标。如果你成功了,你尝试同样的低目标,如果没有 - 你尝试更高的目标。 -
@ohwelppp 仅供参考,您发布的解决方案仅通过了 Codility 上的示例测试。它未通过所有其他测试。