Let me add my 50 cent, how to come up with the score 100% solution.
For me it was hard to understand the ultimate solution, proposed earlier in this thread.
So I started with warm-up solution with score 63%, because its O(NxNxM),
and because it doesn't use the fact that M is quite small value, and there are many duplicates in big arrays
here the key part is to understand how array isSumPossible is filled and interpreted:
how to fill array isSumPossible using numbers in input array:
if isSumPossible[sum] >= 0, i.e. sum is already possible, even without current number, then let's set it's value to 1 - count of current number, that is left unused for this sum, it'll go to our "reserve", so we can use it later for greater sums.
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
}
if isSumPossible[sum] <= 0, i.e. sum is considered not yet possible, with all input numbers considered previously, then let's check maybe
smaller sum sum - number is already considered as possible, and we have in "reserve" our current number (isSumPossible[sum - number] == 1), then do following
else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
here isSumPossible[sum] = 0 means that we have used number in composing sum and it's now considered as possible (>=0), but we have no number in "reserve", because we've used it ( =0)
how to interpret filled array isSumPossible after considering all numbers in input array:
if isSumPossible[sum] >= 0 then the sum is possible, i.e. it can be reached by summation of some numbers in given array
if isSumPossible[sum] < 0 then the sum can't be reached by summation of any numbers in given array
The more simple thing here is to understand why we are searching sums only in interval [0, maxSum/2]:
because if find a possible sum, that is very close to maxSum/2,
ideal case here if we've found possible sum = maxSum/2,
if so, then it's obvious, that we can somehow use the rest numbers in input array to make another maxSum/2, but now with negative sign, so as a result of annihilation we'll get solution = 0, because maxSum/2 + (-1)maxSum/2 = 0.
But 0 the best case solution, not always reachable.
But we, nevertheless, should seek for the minimal delta = ((maxSum - sum) - sum),
so this we seek for delta -> 0, that's why we have this:
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, (maxSum - sum) - sum);
}
}
warm-up solution
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxSum += A[i];
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number : A) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = 1;
} else if (sum >= number && isSumPossible[sum - number] == 1) {
isSumPossible[sum] = 0;
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
and after this we can optimize it, using the fact that there are many duplicate numbers in big arrays, and we come up with the solution with 100% score, its O(Mx(NxM)), because maxSum = NxM at worst case
public int solution(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
if (A.length == 1) {
return A[0];
}
int maxNumber = 0;
int maxSum = 0;
for (int i = 0; i < A.length; i++) {
A[i] = Math.abs(A[i]);
maxNumber = Math.max(maxNumber, A[i]);
maxSum += A[i];
}
int[] count = new int[maxNumber + 1];
for (int i = 0; i < A.length; i++) {
count[A[i]]++;
}
int[] isSumPossible = new int[maxSum + 1];
Arrays.fill(isSumPossible, -1);
isSumPossible[0] = 0;
for (int number = 0; number < maxNumber + 1; number++) {
if (count[number] > 0) {
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
isSumPossible[sum] = count[number];
} else if (sum >= number && isSumPossible[sum - number] > 0) {
isSumPossible[sum] = isSumPossible[sum - number] - 1;
}
}
}
}
int result = Integer.MAX_VALUE;
for (int sum = 0; sum < maxSum / 2 + 1; sum++) {
if (isSumPossible[sum] >= 0) {
result = Math.min(result, maxSum - 2 * sum);
}
}
return result;
}
I hope I've made it at least a little clear