Finding two non-subsequent elements in array which sum is minimal - java

Intro: As far as I could search, this question wasn't asked in SO yet.
This is an interview question.
I am not even specifically looking for a code solution, any algorithm/pseudocode will work.
The problem: Given an integer array int[] A and its size N, find 2 non-subsequent (can't be adjacent in the array) elements with minimal sum. Also the answer must not contain the first or last elements (index 0 and n-1). Also the solution should be in O(n) time and space complexity.
E.g. when A = [5, 2, 4, 6, 3, 7] the answer is 5, since 2+3=5.
When A = [1, 2, 3, 3, 2, 1] the answer is 4, since 2+2=4 and you can't choose either of the 1's since the are at the ends of the array.
Attempt: At first I thought that one of the numbers in the solution must be the smallest one in the array (besides the first and last), but this was refuted quickly with the counter-example A = [4, 2, 1, 2, 4] -> 4 (2+2)
Then I thought that if I find the 2 smallest numbers (besides the first and last) in the array, the solution will be those two. This obviously quickly failed because I can't choose 2 adjacent numbers, and if I have to choose non-adjacent numbers then this is the very definition of the question :).
Finally I thought, well, I will just find the 3 smallest numbers (besides the first and last) in the array, and the solution will have to be two of those, since two of those have to not be adjacent to each other.
This also failed due to A = [2, 2, 1, 2, 4, 2, 6] -> 2+1=3 , which seems to work because I will find 2, 1, 2, but assuming I am finding the 2, 1, 2 in indexes 1, 2, 3 this won't necessarily work (it would if I found specifically the 2 in index 5 but I can't guarantee that unfortunately).
Question:
Now I'm stumped, can anyone come up with a solution/idea that works?

Here is a live javascript implementation of an algorithm that:
finds the 4 smallest elements (excluding first/last element from search)
finds the pairs of these 4 elements that are not adjacent in original array
finds from these pairs the one with the minimal sum
function findMinNonAdjacentPair(a) {
var mins = [];
// quick exits:
if (a.length < 5) return {error: "no solution, too few elements."};
if (a.some(isNaN)) return {error: "non-numeric values given."};
// collect 4 smallest values by their indexes
for (var i = 1; i < a.length - 1; i++) { // O(n)
if (mins.length < 4 || a[i] < a[mins[3]]) {
// need to keep record of this element in sorted list of 4 elements
for (var j = Math.min(mins.length - 1, 2); j >= 0; j--) { // O(1)
if (a[i] >= a[mins[j]]) break;
mins[j+1] = mins[j];
}
mins[j+1] = i;
}
}
// mins now has the indexes to the 4 smallest values
// Find the smallest sum
var result = {
sum: a[mins[mins.length-1]]*2+1 // large enough
}
for (var j = 0; j < mins.length-1; j++) { // O(1)
for (var k = j + 1; k < mins.length; k++) {
if (Math.abs(mins[j] - mins[k]) > 1) { // not adjacent
if (result.sum > a[mins[j]]+a[mins[k]]) {
result.sum = a[mins[j]]+a[mins[k]];
result.index1 = mins[j];
result.index2 = mins[k];
};
if (k < j + 3) return result; // cannot be improved
break; // exit inner loop: it cannot bring improvement
}
}
}
return result;
}
// Get I/O elements
var input = document.getElementById('in');
var output = document.getElementById('out');
var select = document.getElementById('pre');
function process() {
// translate input to array of numbers
var a = input.value.split(',').map(Number);
// call main function and display returned value
output.textContent = JSON.stringify(findMinNonAdjacentPair(a), null, 4);
}
// respond to selection from list
select.onchange = function() {
input.value = select.value;
process();
}
// respond to change in input box
input.oninput = process;
// and produce result upon load:
process();
Type comma-separated list of values (or select one):</br>
<input id="in" value="2, 2, 1, 2, 4, 2, 6"> <=
<select id="pre">
<option value="5, 2, 4, 6, 3, 7">5, 2, 4, 6, 3, 7</option>
<option value="1, 2, 3, 3, 2, 1">1, 2, 3, 3, 2, 1</option>
<option value="4, 2, 1, 2, 4">4, 2, 1, 2, 4</option>
<option value="2, 2, 1, 2, 4, 2, 6" selected>2, 2, 1, 2, 4, 2, 6</option>
</select>
</br>
Output:</br>
<pre id="out"></pre>
The algorithm has a few loops with following big-O complexities:
find 4 smallest values: O(n), as the inner loop runs at most 3 times, which is O(1)
find the smallest sum of non-adjacent pairs has a double loop: in total the body will run at most 4 times = O(1). NB: The number of possible pairs is 6, but the execution is guaranteed to break out of the loops sooner.
So the algorithm runs in O(n).

Find the smallest number beside the first and the last.
Find the second smallest that is not a neighbour of the first one and not the first or last one in the array. Then build the sum.
If the first element is the second or the penultimate element you already have the solution.
Otherwise calculate the sum of both neighbours of the first number. check if its smaller then the first sum
if not: take the first sum
otherwise take the second one
This will always work because if the first sum is not the answer that means the first number cannot be part of the solution. And that on the other hand means, the solution can just be the second sum.

This problem can be solved with about 10 lines of Java code.
You can start with an obvious but inefficient (O(N^2)) solution:
public class Main {
int solve(int[] array) {
int answer = Integer.MAX_VALUE;
for (int i = 3; i < array.length - 1; i++) {
for (int j = 1; j < i - 1; j++) {
if (array[i] + array[j] < answer) {
answer = array[i] + array[j];
}
}
}
return answer;
}
}
But then you can notice that you actually do not need the internal for loop because you can just preserve the minimum and update it with every new element if necessary, which is faster than finding the minimum anew every time. Therefore the final O(N) solution looks like this:
public class Main {
int solve(int[] array) {
int answer = Integer.MAX_VALUE;
int min = array[1];
for (int i = 3; i < array.length - 1; i++) {
min = Math.min(min, array[i - 2]);
if (array[i] + min < answer) {
answer = array[i] + min;
}
}
return answer;
}
}

Find the four smallest and consider all possibilities among those four. The smallest is nonadjacent to at least one of the second, third, or fourth smallest; the only other possibility that could be better is the second and third smallest (assuming that they are nonadjacent).

I think this does not need any deep reasoning, and can be solved in a single pass, keeping the optimal solution of the array elements processed so far:
public static int[] minimumSumOfNonAcjacentElements(int[] a) {
// the result for the sequence a[1:i]
int minSum = Integer.MAX_VALUE;
int minSumElement1 = Integer.MAX_VALUE;
int minSumElement2 = Integer.MAX_VALUE;
// the minimum element eligible for joining with a[i], i.e. from a[1 : i-2]
int minElement = a[1];
int prevElement = a[2]; // a[i - 1]
for (int i = 3; i + 1 < a.length; i++) {
int sum = minElement + a[i];
if (sum < minSum) {
minSum = sum;
minSumElement1 = minElement;
minSumElement2 = a[i];
}
if (prevElement < minElement) {
minElement = prevElement;
}
prevElement = a[i];
}
return new int[] {minSumElement1, minSumElement2};
}
Here's some test code, with the corner cases from OP's question:
private static void test(int minSumIndex1, int minSumIndex2, int... input) {
int[] result = minimumSumOfNonAcjacentElements(input);
if (result[0] == minSumIndex1 && result[1] == minSumIndex2) {
// ok
} else {
throw new AssertionError("Expected: " + minSumIndex1 + ", " + minSumIndex2 + ". Actual=" + Arrays.toString(result));
}
}
public static void main(String[] args) throws Exception {
test(2, 2, 4, 2, 1, 2, 4);
test(1, 2, 2, 2, 1, 2, 4, 2, 6);
test(1, 2, 0, 2, 1, 2, 4, 2, 0);
System.out.println("All tests passed.");
}

Use dynamic programming.
Remove or disregard the first and last elements of your array. Since they cannot participate in the solution, they are not important. Once you've done this, you can also ignore the "must not be the first or last element" constraint since we've already accounted for it.
Find the solution for the first three elements of (what's left of) the array (and without considering the "no first/last element" rule). There is only one solution in this case (array[0] + array[2]), so this is a trivial step.
Memoize the minimal element which is not the last element (i.e. min(array[0], array[1])).
Find the solution for the first four elements. We don't have to redo the whole problem; instead we just have to ask whether introducing the fourth element allows us to produce a smaller solution. We can do this by adding the fourth element to the minimal element we memoized in the previous step, and comparing the sum to the solution we found in the second step.
Update the memoized minimal element so that it is the minimum of the first three elements.
Continue widening and updating in this fashion until we have considered the entire array.
The whole algorithm is O(n), since both widening and updating are constant-time operations. The algorithm can be proved correct by simple induction. O(n) is also a lower bound since we have to consider every element of the array, so this algorithm is optimal.

Algorithm:
Find the minimum, avoiding the end indices. (1 O(n) pass)
Find the minimum, avoiding the end indices and the index of (1) and adjacent indices. (1 O(n) pass)
Find the minimum, avoiding the end indices and the index of (1) (1 O(n) pass)
Find the minimum, avoiding the end indices and the index of (3) and adjacent indices. (1 O(n) pass)
Return the minimum of the sums (1) + (2), (3) + (4), if they exist.
Passes 3 and 4 are meant to pass the case [4, 2, 1, 2, 4] = 4 by finding both 2s.
public static int minSumNonAdjNonEnd(int[] array)
{
// 1. Find minimum
int minIdx1 = -1;
int minValue1 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if (array[i] < minValue1)
{
minIdx1 = i;
minValue1 = array[i];
}
}
// 2. Find minimum not among (1) or adjacents.
int minIdx2 = -1;
int minValue2 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i < minIdx1 - 1 || i > minIdx1 + 1) && (array[i] < minValue2))
{
minIdx2 = i;
minValue2 = array[i];
}
}
boolean sum1Exists = (minIdx1 > -1 && minIdx2 > -1);
int sum1 = minValue1 + minValue2;
// 3. Find minimum not among (1).
int minIdx3 = -1;
int minValue3 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i != minIdx1) && (array[i] < minValue3))
{
minIdx3 = i;
minValue3 = array[i];
}
}
// 4. Find minimum not among(3) or adjacents.
int minIdx4 = -1;
int minValue4 = Integer.MAX_VALUE;
for (int i = 1; i < array.length - 1; i++)
{
if ((i < minIdx3 - 1 || i > minIdx3 + 1) && (array[i] < minValue4))
{
minIdx4 = i;
minValue4 = array[i];
}
}
boolean sum2Exists = (minIdx3 > -1 && minIdx4 > -1);
int sum2 = minValue3 + minValue4;
if (sum1Exists)
{
if (sum2Exists)
return Math.min(sum1, sum2);
else
return sum1;
}
else
{
if (sum2Exists)
return sum2;
else
throw new IllegalArgumentException("impossible");
}
}
This performs 4 linear searches, for a complexity of O(n).
Test cases:
System.out.println(minSumNonAdjNonEnd(new int[] {5, 2, 4, 6, 3, 7}));
System.out.println(minSumNonAdjNonEnd(new int[] {1, 2, 3, 3, 2, 1}));
System.out.println(minSumNonAdjNonEnd(new int[] {4, 2, 1, 2, 4}));
System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 1, 2, 4, 2, 6}));
System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 3, 2}));
5
4
4
3
Exception in thread "main" java.lang.IllegalArgumentException: impossible

I don't know if my solution is correct because I just tested it with the data in the OP, and I don't even know if this is better or worse than the other ideas but I wanted to try it.
static void printMinimalSum(int[] A) {
// Looking for mins so we init this with max value
int[] mins = new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
// Indices, used just to print the solution
int[] indices = new int[]{-1, -1, -1};
// If the array has length 5 then there's only one solution with the 2nd and 4th elements
if (A.length == 5) {
mins[0] = A[1];
indices[0] = 1;
mins[1] = A[3];
indices[1] = 3;
} else {
// Loop on the array without considering the first and the last element
for (int i = 1; i < A.length - 1; i++) {
// We consider each element which is smaller than its neighbours
if ((i == 1 && A[i] < A[i + 1]) // 1: first element, compare it with the second one
|| (i == A.length - 2 && A[i] < A[i - 1]) // 2: last element, compare it with the previous one
|| (A[i] < A[i + 1] && A[i] < A[i - 1])) { // 3: mid element, compare it with both neighbors
// If the element is "legal" then we see if it's smaller than the 3 already saved
if (A[i] < mins[0]) {
mins[0] = A[i];
indices[0] = i;
} else if (A[i] < mins[1]) {
mins[1] = A[i];
indices[1] = i;
} else if (A[i] < mins[2]) {
mins[2] = A[i];
indices[2] = i;
}
}
}
}
// Compute the 3 sums between those 3 elements
int[] sums = new int[]{Math.abs(mins[0]+mins[1]), Math.abs(mins[0]+mins[2]), Math.abs(mins[1]+mins[2])};
// Find the smaller sum and print it
if (sums[0] < sums[1] || sums[0] < sums[2]){
System.out.println("Sum = " + sums[0] + " (elements = {" + mins[0] + "," + mins[1] + "}, indices = {" + indices[0] + "," + indices[1] + "}");
} else if (sums[1] < sums[0] || sums[1] < sums[2]){
System.out.println("Sum = " + sums[1] + " (elements = {" + mins[0] + "," + mins[2] + "}, indices = {" + indices[0] + "," + indices[2] + "}");
} else {
System.out.println("Sum = " + sums[2] + " (elements = {" + mins[1] + "," + mins[2] + "}, indices = {" + indices[1] + "," + indices[2] + "}");
}
}
public static void main(String[] args) {
printMinimalSum(new int[]{5, 2, 4, 6, 3, 7});
printMinimalSum(new int[]{1, 2, 3, 3, 2, 1});
printMinimalSum(new int[]{4, 2, 1, 2, 4});
printMinimalSum(new int[]{2, 2, 1, 2, 4, 2, 6});
}
Output is:
Sum = 5 (elements = {2,3}, indices = {1,4}
Sum = 4 (elements = {2,2}, indices = {1,4}
Sum = 4 (elements = {2,2}, indices = {1,3}
Sum = 3 (elements = {1,2}, indices = {2,5}
which seems fine.

edit: you're right, I completely ignored the adjacency constraint.
luckily I've thought of a solution.
The algorithm goes like this:
You run once over the array to find the smallest (O(n))
You run a second time to find the second smallest (O(n))
If second smallest is not adjacent to smallest we're done(O(1) - just an index check)
Otherwise run a third time to find third smallest (still O(n))
If not adjacent to smallest return smallest and third smallest
otherwise return second and third smallest

Elaborating on the above answer, you'd need a modified insertion-sort to track the smallest four values and the corresponding indexes (an array of 4 elements for each).
Once found the solution would be the first pair whose difference in indexes would be more than 1 and whose sum is the least.
The solution being one of (0,1) or (0,2) or (0,3) or (1,2) or (1,3) or (2,3) where the values indicate the indexes of the array that in turn tracks the position of the actual elements in the array.
Also you'd need to handle the special case for array-length 5 (arr\[1]+arr[3]) and an error for those arrays less than 5.

I think this should work:
Find the minimum 3 element and their indices. Since all of them can't be adjacent choose 2 among them.
If all of them are adjacent and the minimum number is in the middle of them, iterate through all elements, find the forth minimum element, choose minimum of min1+min4, min2+min3, whichever is smaller.
You can do this in one iteration too.

I have used dynamic programming to solve it.
Idea is to first create the array which tracks the minimum found till now as below:
Input array = [1, 3, 0, 5, 6]
Minimum array = [1, 1, 0, 0, 0]
Now using the minimum array and the input array we can use below:
DP[i] = min(DP[i-1], min(first_data, second_data))
where DP[i] means the minimum found till now which is sum of two previous alternate elements.
first_data = sum of current element in input array + sum of current-2 element in minimum array
second_data = sum of current-1 element in input array + sum of current-3 element in minimum array
import random
def get_min(numbers):
#disregard the first and last element
numbers = numbers[1:len(numbers)-1]
#for remembering the past results
DP = [0]*len(numbers)
#for keeping track of minimum till now found
table = [0]*len(numbers)
high_number = 1 << 30
min_number = numbers[0]
table[0] = min_number
for i in range(0, len(numbers)):
DP[i] = high_number
for i in range(1, len(numbers)):
if numbers[i] < min_number:
min_number = numbers[i]
table[i] = numbers[i]
else:
table[i] = min_number
for i in range(0, len(numbers)):
min_first, min_second = high_number, high_number
if i >= 2:
min_first = numbers[i] + table[i-2]
if i >= 3:
min_second = numbers[i-1] + table[i-3]
if i >= 1:
DP[i] = min(min(DP[i-1], min_first), min_second)
return DP[len(numbers)-1]
input = random.sample(range(100), 10)
print(input)
print(get_min(input))

How about that: you find k smallest numbers (or more precisely their indices) (k big enough, let say 10). It is sure, that the wanted pair is between them. Now you just check the possible 50 pairs and select the best which satisfies the constraints.
You don't need 10, less would do - but more than 3 :)
Edit: finding k smallest numbers is O(n), because you just keep the best 10 for example in a heap (add new element, delete maximum O(k*logk)=O(1) operations).
Then there will be a pair which satisfy the constraints (not next to each other). It is also clear that, if you build the sum with an element not from those k, it would be bigger than the best pair chosen from those k elements.
Checking at most k*k pairs is also O(1), thus the whole running time is O(n).

Here is the python implementation in O(N) time complexity
import math
def minSum(array):
_min = array[1]
result = math.inf
for i in range(3, len(array) - 1):
_min = min(_min, array[i-2])
if (_min + array[i]) < result:
result = _min + array[i]
return result

As we only need to track the minimum sum of two no adjacent values, we could do it by iterating over the array excluding the first and last element and keeping the track of min values and minimum sum. current min value will the two index before current value. For example if we are checking the current index i then minValue is the minimum value from index 1 to i-2.
Code:
int minSum(int[] A){
int minSum=Integer.MAX_VALUE;
int min= Integer.MAX_VALUE;
for(int i=3; i<A.length-1; i++){
min= Math.min(A[i-2], min);
minSum = Math.min(min+A[i], minSum);
}
return minSum;
}

Related

Find all combinations of an array and get top k sum elements

I have an array of numbers say [1,2,3,1,1000] , now I want to get all possible combinations of this array and calculate its sum. Combinations are valid such that two combinations have different subset of elements. Then order all the sum values in descending order and get the top k elements.
Example:
[1,2,3,1,1000]
Combinations:
Duplicates of earlier ones are striked out, for example (3,1) matches the earlier (1,3).
(), (1), (2), (3), (1), (1000), (1,2), (1,3), (1,1), (1,1000), (2,3), (2,1), (2,1000), (3,1), (3,1000), (1,1000), (1,2,3), (1,2,1), (1,2,1000), (1,3,1), (1,3,1000), (1,1,1000), (2,3,1), (2,3,1000), (2,1,1000), (3,1,1000), (1,2,3,1), (1,2,3,1000), (1,2,1,1000), (1,3,1,1000), (2,3,1,1000), (1,2,3,1,1000)
And the corresponding sums:
0, 1, 2, 3, 1, 1000, 3, 4, 2, 1001, 5, 3, 1002, 4, 1003, 1001, 6, 4, 1003, 5, 1004, 1002, 6, 1005, 1003, 1004, 7, 1006, 1004, 1005, 1006, 1007
Getting top k=3, sums = 1007, 1006, 1005
So output is [1007, 1006, 1005].
Constraints:
Array size n = 1 to 105
Array elements -109 to 109
k ranges from 1 to 2000
This is my code, reference taken from here:
static List<Long> printDistSum(int arr[]) {
List<Long> list = new ArrayList<>();
int n = arr.length;
// There are totoal 2^n subsets
long total = (long) Math.pow(2, n);
// Consider all numbers from 0 to 2^n - 1
for (int i = 0; i < total; i++) {
long sum = 0;
// Consider binary representation of
// current i to decide which elements
// to pick.
for (int j = 0; j < n; j++)
if ((i & (1 << j)) != 0)
sum += arr[j];
// Print sum of picked elements.
list.add(sum);
}
return list;
}
This code works for small range of inputs but times out for large range of inputs. How to solve this program.
I probably have solution that should be good enough. It has time complexity O(n * k * log(k)).
First we need to calculate max sum - sum of all positive values.
Next we need to iterate over positive values, from smallest to largest. For each of these values we calculate sums of new combinations (at the start we have one combination with max sum).
New combinations will not contains given value so we need to substract it from sum.
At the end we need to iterate over negative values. These values are not belongs to combinations from previous step so we need to add these values to sums.
In every iteration are needed only k maximum sums. I used the PriorityQueue to store these sums. That class use heap data structure so adding/removing values has logarithmic time.
Code:
private static long[] findSums(int[] array, int k) {
long maxSum = Arrays.stream(array).filter(it -> it >= 0).sum();
int[] positives = Arrays.stream(array).filter(it -> it >= 0).sorted().toArray();
int[] negatives = Arrays.stream(array).filter(it -> it < 0).sorted().toArray();
// sort time complexity is O(n*log(n))
PriorityQueue<Long> sums = new PriorityQueue<>(k); // priority queue is implemented using heap so adding element has time complexity O(log(n))
sums.add(maxSum); // we start with max sum - combination of all positive elements
int previous = Integer.MIN_VALUE;
Long[] previousAddedSums = {};
Long[] sumsToIterate;
// iterate over positive values
for (int i = 0; i < positives.length; i++) {
if (positives[i] == previous) {
sumsToIterate = previousAddedSums;
} else {
sumsToIterate = sums.toArray(new Long[sums.size()]);
}
previousAddedSums = new Long[sumsToIterate.length];
for (int j = 0; j < sumsToIterate.length; j++) {
long newSum = sumsToIterate[j] - positives[i];
// new sum is calculated - value positives[i] is removed from combination (subtracted from sum of that combination)
sums.add(newSum);
previousAddedSums[j] = newSum;
if (sums.size() > k) {
sums.poll(); // only first k maximum sums are needed at the moment
}
}
previous = positives[i];
}
previous = Integer.MAX_VALUE;
// iterate over negative values in reverse order
for (int i = negatives.length - 1; i >= 0; i--) {
if (negatives[i] == previous) {
sumsToIterate = previousAddedSums;
} else {
sumsToIterate = sums.toArray(new Long[sums.size()]);
}
previousAddedSums = new Long[sumsToIterate.length];
for (int j = 0; j < sumsToIterate.length; j++) {
long newSum = sumsToIterate[j] + negatives[i]; // value negatives[i] is added to combination (added to sum of that combination)
sums.add(newSum);
previousAddedSums[j] = newSum;
if (sums.size() > k) {
sums.poll();
}
}
previous = negatives[i];
}
long[] result = new long[sums.size()];
for (int i = sums.size() - 1; i >=0 ; i--) {
result[i] = sums.poll();
}
// get sums from priority queue in proper order
return result;
// this whole method has time complexity O(n * k * log(k))
// k is less than or equal 2000 so it should be good enough ;)
}
Demo: https://ideone.com/yf6POI
Edit: I have fixed my solution. Instead of iterating over distinct values I check if current value is same like previous. In that case I use combinations (sums) created in previous step. This prevents from creating duplicates of combinations.
I'm sorry if I didn't explain this well enough. I don't have experience in describing algorithmic / mathematical things in english.
Pls ignore all previous posts cuz they are all wrong.
Intuitively, we gotta use backtrack to find all desired combos, but it's impossible to backtrack on 10^5 elements.
Constraint 1 <= n <= 10^5 alludes that our algorithm bottlenecked by O(nlogn) sorting
Constraint 1 <= k <= min(2000,2^n) alludes that we can backtrack on k elements since k is less than 11. 2^11=2024/log(2000)=11 -- actually this "2^n" gives away solution :)
My algorithm (nlog(n) + 2^k)
sort the array
Record the highest score combo which is the sum of all positive integers
Find a window in the sorted array of math.min(log(k)--which is less than 11,n) elements -- worst case, this window consists of the
lowest 11 absolute values in the sorted array. Several approaches to
achieve that, since the candidates must be inside 22 elements
window(11 smallest positive values + 11 biggest negative values), we
can use PriorityQueue of size 11 scanning over these 22 elements. or
we can use two pointers to find the sliding window of size 11.
backtrack on this 11 absolute value elements window, find sum of each combo and put them into a size k/k-1 PriorityQueue. (k is for
the case that there's no positive elements)
result is the sum of all positive integers plus (sum deducted by each of k-1 elements in PriorityQueue).
I was also asked the same question yesterday but sadly I was not able to solve it yesterday. I have tried solving it today and think I have the answer today.
First of all I don't think that different subsets mean different costs in a set i.e in array of [1,2,3,1] both subsets are valid => [1,2,3] and [2,3,1] as they both use different 1's. Now here is my solution keeping this in mind. But if you really want to keep distinct elements in set then you can simply remove the multiple elements and do partial_sort then.
Logic
Store sum of all +ve nos. in a variable, say maxsum.
Convert the negative nos. to their absolute values.
Get lowest min(k-1, n) elements in sorted order.
Find all their combinations and subtract them from the maxsum.
While finding all their combinations we only need lowest k-1 combos. So we have to find a way to keep the number of combinations to that. For that use a sorted data structure and limit its size to k and then for every element in the sorted array iterate through the combos and add those combos to the sorted data structure if the end element of that data structure is greater. Also pop the end element after that.
For taking care of the above point I am using 2 vectors since the order already remains sorted.
The proposed solution has time complexity of O(n*log(k) + k^2).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int ll;
template <class T>
void print(vector<T> topSumm)
{
for (ll itr : topSumm)
cout << itr << '\t';
cout << '\n';
}
vector<ll> mergeSortedArrays(vector<ll> &minns, vector<ll> &temp)
{
vector<ll> ans(minns.size() + temp.size());
int i{0}, j{0}, k{0};
while (i < minns.size() && j < temp.size())
{
if (temp[j] < minns[i])
ans[k++] = temp[j++];
else
ans[k++] = minns[i++];
}
while (i < minns.size())
ans[k++] = minns[i++];
while (j < temp.size())
ans[k++] = temp[j++];
return ans;
}
vector<ll> topKSum(vector<int> &arr, int k)
{
int n{(int)arr.size()};
ll maxSumm{0};
for (int i{0}; i < n; ++i)
{
if (arr[i] > 0)
maxSumm += arr[i];
else
arr[i] = -arr[i];
}
int nk{min(k - 1, n)};
partial_sort(arr.begin(), arr.begin() + nk, arr.end());
vector<ll> minns{0, maxSumm};
ll summ{};
bool breakOuter{false};
for (int i{0}; i < nk; ++i)
{
vector<ll> temp;
for (ll nums : minns)
{
summ = nums + arr[i];
if (minns.size() + temp.size() < k)
temp.push_back(summ);
else
{
if (minns.back() > summ)
{
minns.pop_back();
temp.push_back(summ);
}
else
{
if (nums == 0)
breakOuter = true;
break;
}
}
}
if (breakOuter)
break;
minns = mergeSortedArrays(minns, temp);
}
vector<ll> ans(k);
int i{0};
for (ll nums : minns)
ans[i++] = maxSumm - nums;
return ans;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n, k;
cin >> n >> k;
vector<int> arr(n);
ll maxSumm{0};
for (int i{0}; i < n; ++i)
cin >> arr[i];
vector<ll> topSums = topKSum(arr, k);
print<ll>(topSums);
}
return 0;
}

How to distribute maximum chocolates among k students

I was asked this question in an interview -
Maximum number of chocolates to be distributed equally among k students
Given n boxes containing some chocolates arranged in a row. There are k number of students. The problem is to distribute maximum number of chocolates equally among k students by selecting a consecutive sequence of boxes from the given lot. Consider the boxes are arranged in a row with numbers from 1 to n from left to right. We have to select a group of boxes which are in consecutive order that could provide maximum number of chocolates equally to all the k students. An array arr[] is given representing the row arrangement of the boxes and arr[i] represents number of chocolates in that box at position i.
Examples:
Input : arr[] = {2, 7, 6, 1, 4, 5}, k = 3
Output : 6
The subarray is {7, 6, 1, 4} with sum 18.
Equal distribution of 18 chocolates among 3 students is 6.
Note that the selected boxes are in consecutive order with indexes {1, 2, 3, 4}.
I found the solution in Geekforgeeks --
static int maxNumOfChocolates(int arr[], int n, int k)
{
// Hash table
HashMap <Integer,Integer> um = new HashMap<Integer,Integer>();
// 'sum[]' to store cumulative sum, where
// sum[i] = sum(arr[0]+..arr[i])
int[] sum=new int[n];
int curr_rem;
// To store sum of sub-array having maximum sum
int maxSum = 0;
// Building up 'sum[]'
sum[0] = arr[0];
for (int i = 1; i < n; i++)
sum[i] = sum[i - 1] + arr[i];
// Traversing 'sum[]'
for (int i = 0; i < n; i++) {
// Finding current remainder
curr_rem = sum[i] % k;
// If true then sum(0..i) is divisible
// by k
if (curr_rem == 0) {
// update 'maxSum'
if (maxSum < sum[i])
maxSum = sum[i];
}
// If value 'curr_rem' not present in 'um'
// then store it in 'um' with index of its
// first occurrence
else if (!um.containsKey(curr_rem) )
um.put(curr_rem , i);
else
// If true, then update 'max'
if (maxSum < (sum[i] - sum[um.get(curr_rem)]))
maxSum = sum[i] - sum[um.get(curr_rem)];
}
// Required maximum number of chocolates to be
// distributed equally among 'k' students
return (maxSum / k);
}
This works but I needed some explanation WHY this works, i could understand the part --
if (curr_rem == 0) {
// update 'maxSum'
if (maxSum < sum[i])
maxSum = sum[i];
}
Will work if consecutive sum of array elements are divisible by k, but trying to understand this part --
else if (!um.containsKey(curr_rem) )
um.put(curr_rem , i);
else
// If true, then update 'max'
if (maxSum < (sum[i] - sum[um.get(curr_rem)]))
maxSum = sum[i] - sum[um.get(curr_rem)];
Any explanation here would be very helpful
Very short explanation:
In the main loop you look at all sums from index 0 to the current index. If you have a remainder you have to check if you can “make it go away” by starting from another index than 0.
Checking for that is exactly the same thing as finding an equal remainder stored before.
E. g. you had remainder 3 at index 7. If you have remainder 3 again at index 13 you have no remainder by adding between 7 and 13.
What remains is taking the max. sum of all solutions found.
class Solve
{
//arr -> chocolates values, n = len(arr), k->no of students
//Idea:
//1.to use prefix sum and find the subarray whose sum is divisible by k.
//2.when we find the subarray we try to find the sum of this subarray by using
//precomputation sum of arr
//3.we keep a max variable to record the max sum and return max that's it!!!!!
static int maxNumOfChocolates(int arr[], int n, int k)
{
if(n==0)return 0;
int []prefix = new int[n];
prefix[0] = arr[0];
for(int i=1;i<n;++i){
prefix[i] = prefix[i-1]+arr[i];
}
HashMap<Integer,Integer> map = new HashMap<>();
map.put(0,-1);
int ans = -1;
int sum = 0;
for(int i=0;i<n;i++){
sum+=arr[i];
if(map.containsKey(sum%k)){
int j = map.get(sum%k);
ans = Math.max(ans,prefix[i]-((j>=0)?prefix[j]:0));
}
map.putIfAbsent(sum%k,i);
}
if(ans==-1)return -1;
return ans/k;
}
}

Complexity of following algorithm for the Three-Sum-Problem

Given is a sorted list (a[0],...,a[n-1]) of n integers. I need to find three different indices p, q, r such that the triplet (a[p],a[q],a[r]) satisfies the equation a[p]+a[q]+a[r]=0. Also, the sorted list can contain the same number more than once. The algorithm that is required needs to be quadratic.
I have found a solution (I'm definitely not saying that it is the most efficient one), but I'm quite sure that it's not quadratic (2 for-loops and a while-loop). Here is my code:
public ThreeSumIndices searchTriplet(List<Integer> list) {
for(int i=0; i<list.size()-1; i++){
int w = -list.get(i);
for(int j=i+1; j<list.size(); j++){
int k = 1;
while(j+k<list.size() && list.get(j)+list.get(j+k)!=w){
k++;
}
if(j+k==list.size()){
k = 1;
} else if(list.get(j)+list.get(j+k)==w){
return new ThreeSumIndices(i,j,j+k);
}
}
}
return null; //no indices found.
}
ThreeSumIndices is a seperate class. It returns the indices we're looking for in the form (p,q,r). Constructor parameters are three integer (= three indices).
Example: (-5, 1, 2, 3, 7) --> (0,2,3).
I'm fairly new to complexity analysis, so I was wondering whether my guess of this algorithm not being quadratic was correct or not.
If so, is there a way to get rid of a loop? Or maybe there's an alternative, but more efficient algorithm?
Thanks.
If the array is sorted then all you need to do is the following:
Run loop from i=0 to n-2.
Initialize two index variables l=i+1 and r=n-1
while:l<r, if ((sum = arr[i]+arr[l]+arr[r]) == 0):You got your answer
If sum is less than zero, increment l (l++), otherwise decrement r (r--)
scan all the elements.
for (int i=0; i < n-1; i++) {
int l = i + 1;
int r = n - 1;
int x = arr[i];
while (l < r){
if (x + arr[l] + arr[r] == 0) print()
else if(x + arr[l] + arr[r] < 0) l++;
else r--;
}
}
The time complexity of this code will be O(n^2) with space complexity of O(1)
I had to solve this for a coding interview, here's my python solution which has quadratic time complexity.
from collections import Counter
class Solution(object):
def threeSum(self, nums):
res = []
counts = Counter(nums)
num_counts = sorted(counts.items())
# If we pick 3 of the same nums
if counts[0] >= 3:
res.append([0] * 3)
for i, (first, first_count) in enumerate(num_counts):
# Pick two of these and one greater
if first_count >= 2 and first < 0 and -(first * 2) in counts:
res.append([first, first, -(first * 2)])
# Pick one and two greater
for j in range(i + 1, len(num_counts)):
second, second_count = num_counts[j]
# Pick two of these as second and third num
if second_count >= 2 and -first == 2 * second:
res.append([first, second, second])
# Pick this as second num and third which is greater
third = -(first + second)
if third > second and third in counts:
res.append([first, second, third])
return res
Basically, it counts the occurrences then sorts (number, count) tuples so that
[-1, 0, 1, 2, -1, -4]
becomes
[(-4, 1), (-1, 2), (0, 1), (1, 1), (2, 1)]
We then iterate trying to pick each number twice and third greater if possible and add that to the result. Then, we pick a number once and try to find two greater numbers which sum to zero.

Sorting 2 sets of scores with array in java

I wrote following code to rank 2 sets of scores:
public class ScoreRanking {
public static void main(String[] args) {
int[] score1 = { 9, 3, 6, 19 };
int[] score2 = { 3, 3, 3, 3, 3, 5, 1 };
int[] rank1 = sortScores(score1);
int[] rank2 = sortScores(score2);
for (int i = 0; i < rank1.length; i++) {
System.out.println((i + 1) + ": " + rank1[i]);
}
System.out.println("_________\n");
for (int j = 0; j < rank2.length; j++) {
System.out.println((j + 1) + ": " + rank2[j]);
}
}
static int[] sortScores(int[] sort) {
for (int i = 0; i < sort.length - 1; i++) {
if (sort[i] < sort[i + 1]) {
continue;
} else {
int temp = sort[i];
sort[i] = sort[i + 1];
sort[i + 1] = temp;
}
}
return sort;
}
}
The first set is ordered but the second isn't. I played around with the brackets, tried a different order (int score 1... int rank1... int score2... int rank2... / int score2..., int rank2... int score1... int rank1...) - but the score2 is always "ignored". Can someone please tell me why?
I made a cold debug and I think that you're problem is with the sortScores method.
if I'm not wrong the rank2 array is sorted like this {3, 3, 3, 3, 3, 1, 5}.
The main problem that your facing is that your algorithm only change the i and i+1 elements, but what happen when your i+2 elements in lower than the i element, there the method fails doesn't do the job correctly.
If you want to develop a sorting algorithm by you I recommend that first make sort an array with 5 or 4 elements in paper and them try to understand what are you doing, them write the steps in pseudo code and finally translate into Java is longer but I guaranteed you that it will be more satisfactory, but if you want to sort the array write now them you should check the sorting algorithms on Wikipedia and take a special look on the Insertion sort is easy to understand and doesn't have much complexity.
At last, if you want to used java libraries to save time and code them you should check the Array class the sort(int[] a) method.
I hope that my answers help you to understand better this world.
Bye, Greetings from Venezuela
PD. The Insertion Sort Algorithm is this one:
public static void insertionSort (int[] array) {
for (int i=1; i < array.length; i++) {
int aux = array[i];
int j;
for (j=i-1; j >= 0 && array[j] > aux; j--){
array[j+1] = array[j];
}
array[j+1] = aux;
}
}
I'd suggest you looked up sorting algorithms as dev8080 suggested. Your sorting algorithm only goes through the list once, which is unheard of. And while your sorting algorithm may end up working for the first array, for the second array it has problems:
if (sort[i] < sort[i + 1])
With this condition you're swapping values even if they're equal, which means you're unnecessarily swapping those 3's. This might not have a visible effect, but it is a wasteful operation, and for more complex objects could produce undesirable effects.
At any rate your code only swaps the values when
sort[i] = 5
sort[i + 1] = 1
because this is the only place where the first value is less than the second one. This is why you get the result
1: 3
2: 3
3: 3
4: 3
5: 3
6: 1
7: 5
It looks like you're trying to implement an variant of bubble sort, in which case you should check out this link:
https://en.wikipedia.org/wiki/Bubble_sort
keep in mind bubble sort is not very efficient for larger lists

Even numbers before odd numbers using array and only one loop

I attempted the problem. I am not getting the right solution. Kindly help.
Problem : Return an array that contains the exact same numbers as the given array, but rearranged so that all the even numbers come before all the odd numbers. Other than that, the numbers can be in any order. You may modify and return the given array, or make a new array.
evenOdd([1, 0, 1, 0, 0, 1, 1]) → [0, 0, 0, 1, 1, 1, 1]
evenOdd([3, 3, 2]) → [2, 3, 3]
evenOdd([2, 2, 2]) → [2, 2, 2]
public int[] evenOdd(int[] nums) {
int l = nums.length;
if(l<2)
return nums;
int j=l-1;
for(int i=0;i<l;i++)
{
if(nums[i]%2==1)
{
while(j>i && nums[j]%2!=0) {
j--;
}
int t=nums[i];
nums[i]=nums[j];
nums[j]=t;
j--;
}
}
return nums;
}
As order of array doesn't matter and you need only one loop, you can try below function,
public int[] evenOdd(int[] nums) {
if (nums.length < 2) return nums;
int[] result = new int[nums.length];
int even = 0;
int odd = nums.length - 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 0)
result[even++] = nums[i];
else
result[odd--] = nums[i];
}
return result;
}
You're actually super close. If you just wrap this code that you have:
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
j--;
inside of an if
if (j > i)
your code actually works.
Not sure if this is "cheating", but you could create 2 arrays, 1 to hold the evens and 1 to hold the odds. In your loop, you would copy all the numbers of each value to their even or odd array, and then after the loop, join/merge the arrays together in a new array and return the new array.
If performance is not too important, you could use a stream:
static int[] evenOdd(int[] nums) {
return Arrays.stream(nums)
.boxed()
.sorted(Comparator.comparingInt(i -> i % 2))
.mapToInt(i -> i)
.toArray();
}
Unfortunately, IntStream doesn't have a sorted method that takes a Comparator (only a parameterless method, that's why you have to box and unbox to use Stream.sorted(Comparator).
Here is a code that takes O(N) time and O(1) space to accomplish your task. I apologise for the Python code.
arr = list(map(int, raw_input().split()))
i = 0
j = len(arr) - 1
while i<j:
if arr[i]%2 != 0 and arr[j]%2 == 0:
t = arr[i]
arr[i] = arr[j]
arr[j] = t
i = i + 1
j = j -1
elif arr[i]%2 == 0 and arr[j]%2 == 0:
i = i + 1
elif arr[i]%2 == 0 and arr[j]%2 != 0:
j = j - 1
else:
j = j - 1
print arr
Explanation: The code logic is quite self explanatory. We have two counters, i starting from left end and j starting from right end. If the left counter is pointing to an even then we just increment it, as it is in its correct place. [Remember you want to shift evens to the left. So this even is already in the left side of the array.So we just increment i]. Please look at the code logic to find out what actions are taken based on the current pointers on an even or an odd element.
For example:
If we find i pointing to an odd and j pointing to an `even, then we swap and move both pointers. This is understandable, I hope
The above solution is in-place and takes O(1) space and O(N) time... Hope this helps!!

Categories