How to find maximum sum of smallest and second smallest elements chosen from all possible subarrays - algorithm

Given an array, find maximum sum of smallest and second smallest elements chosen from all possible subarrays. More formally, if we write all (nC2) subarrays of array of size >=2 and find the sum of smallest and second smallest, then our answer will be maximum sum among them.
Examples: Input : arr[] = [4, 3, 1, 5, 6] Output : 11`
Subarrays with smallest and second smallest are,
[4, 3] smallest = 3 second smallest = 4
[4, 3, 1] smallest = 1 second smallest = 3
[4, 3, 1, 5] smallest = 1 second smallest = 3
[4, 3, 1, 5, 6] smallest = 1 second smallest = 3
[3, 1] smallest = 1 second smallest = 3
[3, 1, 5] smallest = 1 second smallest = 3
[3, 1, 5, 6] smallest = 1 second smallest = 3
[1, 5] smallest = 1 second smallest = 5
[1, 5, 6] smallest = 1 second smallest = 5
[5, 6] smallest = 5 second smallest = 6
Maximum sum among all above choices is, 5 + 6 = 11
This question is on GFG but I didn't understand its explanation.
Please anybody gives its solution in O(n) time complexity.

The question is:
Why are we guaranteed to always find the maximum sum if we only look at all subarrays of length two?
To answer that question, lets assume we have some array A. Inside that array, obviously, there has to be at least one subarray S for which the smallest and second smallest elements, let's call them X and Y, sum up to our result.
If these two elements are already next to each other, this means that there is a subarray of A of length two that will contain X and Y, and thus, if we only look at all the subarrays of length two, we will find X and Y and output X+Y.
However, the question is: Is there any way for our two elements X and Y to not be "neighbors" in S? Well, if this was the case, there obviously would need to be other numbers, lets call them Z0, Z1, ..., between them.
Obviously, for all these values, it would have to hold that Zi >= X and Zi >= Y, because in S, X and Y are the smallest and second smallest elements, so there can be no other numbers smaller than X or Y.
If any of the Zi were bigger than X or Y, this would mean that there would be a subarray of A that only included this bigger Zi plus its neighbor. In this subarray, Zi and its neighbor would be the smallest and second smallest elements, and they would sum up to a larger sum than X+Y, so our subarray S would not have been the subarray giving us our solution. This is a contradiction to our definition of S, so this can not happen.
So, all the Zi can not be smaller than X or Y, and they can not be bigger than X or Y. This only leaves one possibility: For X == Y, they could all be equal. But, in this case, we obviously also have a subarray of length 2 that sums up to our correct result.
So, in all cases, we can show that there has to be a subarray of length two where both elements sum up to our result, which is why the algorithm is correct.

At first place you didn't understand the question! if you consider all sub-arrays carefully, at the end you can see all sub-arrays are related; in other words we are considering the result of previous sub-array into the current sub-array
Subarrays with smallest and second smallest are,
[4, 3] smallest = 3 second smallest = 4
[4, 3, 1] smallest = 1 second smallest = 3
[4, 3, 1, 5] smallest = 1 second smallest = 3
[4, 3, 1, 5, 6] smallest = 1 second smallest = 3
[3, 1] smallest = 1 second smallest = 3
[3, 1, 5] smallest = 1 second smallest = 3
[3, 1, 5, 6] smallest = 1 second smallest = 3
[1, 5] smallest = 1 second smallest = 5
[1, 5, 6] smallest = 1 second smallest = 5
[5, 6] smallest = 5 second smallest = 6
Maximum sum among all above choices is, 5 + 6 = 11
From each subarray:
1. we are taking the current largest value and if it is greater than previous largest we are replacing, and previous largest eventually becomes second most largest.
2. we are repeating this steps for every possible subarray (increasing in terms of index).
3. and at the end you can see, we are taking first-most and second-most value from the array.
so checking every-pair of values from the array reduced your overall complexity in O(N)
int res = arr[0] + arr[1]; //O(1)+O(1)
for (int i=1; i<N-1; i++) // O(N-2) -> O(N)
res = max(res, arr[i] + arr[i+1]); //O(1)+O(1)+O(1)
Overall complexity: O(N).

Related

given a positive number as a dividend, find out a subset from a given array, so that can get the minimum remainder

Here are some examples:
given number: 31
given array: [4, 5, 6, 7]
then the result subset will be [4] or [5] as 31 = 4 * 4 + 3 * 5, the remainder is 0,
but if given number: 31
given array: [4, 5, 6, 7, 8]
then the result subset will be [7, 8] as 31 = 3 * 8 + 1 * 7, the remainder is 0, which is the minimum one.
or if given number: 67
given array: [4, 5, 6, 9, 10, 12]
then the result subset will be [4, 9] as 67 = 7 * 9 + 1 * 4, the remainder is 0, which is the minimum one.
So what I want to ask is that if there is an algorithm to find out such subset from a given array, so that one can get the minimum remainder...
You can consider this task as variant of coin change problem (having sum and some coin nominals, choose the smallest number of coin to compose the sum).
I might be solved with dynamic programming:
Make array A of size sum, fill A[0] with 0, other entries with extreme large value.
For every coin C walk through array.
Intermediate sum i might be composed using coin C and sum i-C, so check
whether adding C to A[i-C] will diminish number of coin nominals used for A[i] and replace A[i] in positive case.
After all A[sum] will contain the smallest number of nominals. If it contain initial large value, scan lesser entries (A[sum-1] and so on).

no. of permutation of number from 1 to n in which i >i+1 and i-1

for a given N how many permutations of [1, 2, 3, ..., N] satisfy the following property.
Let P1, P2, ..., PN denote the permutation. The property we want to satisfy is that there exists an i between 2 and n-1 (inclusive) such that
Pj > Pj + 1 ∀ i ≤ j ≤ N - 1.
Pj > Pj - 1 ∀ 2 ≤ j ≤ i.
like for N=3
Permutations [1, 3, 2] and [2, 3, 1] satisfy the property.
Is there any direct formula or algorithm to find these set in programming.
There are 2^(n-1) - 2 such permutations. If n is the largest element, then the permutation is uniquely determined by the nonempty, proper subset of {1, 2, ..., n-1} which lies to the left of n in the permutation. This answer is consistent with the excellent answer of #גלעדברקן in view of the well-known fact that the elements in each row of Pascal's triangle sum to a power of two (hence the part of the row between the two ones is two less than a power of two).
Here is a Python enumeration which generates all n! permutations and checks them for validity:
import itertools
def validPerm(p):
n = max(p)
i = p.index(n)
if i == 0 or i == n-1:
return False
else:
before = p[:i]
after = p[i+1:]
return before == sorted(before) and after == sorted(after, reverse = True)
def validPerms(n):
nums = list(range(1,n+1))
valids = []
for p in itertools.permutations(nums):
lp = list(p)
if validPerm(lp): valids.append(lp)
return valids
For example,
>>> validPerms(4)
[[1, 2, 4, 3], [1, 3, 4, 2], [1, 4, 3, 2], [2, 3, 4, 1], [2, 4, 3, 1], [3, 4, 2, 1]]
which gives the expected number of 6.
On further edit: The above code was to verify the formula for nondegenerate unimodal permutations (to coin a phrase since "unimodal permutations" is used in the literature for the 2^(n-1) permutations with exactly one peak, but the 2 which either begin or end with n are arguably in some sense degenerate). From an enumeration point of view you would want to do something more efficient. The following is a Python implementation of the idea behind the answer of #גלעדברקן :
def validPerms(n):
valids = []
nums = list(range(1,n)) #1,2,...,n-1
snums = set(nums)
for i in range(1,n-1):
for first in itertools.combinations(nums,i):
#first will be already sorted
rest = sorted(snums - set(first),reverse = True)
valids.append(list(first) + [n] + rest)
return valids
It is functionally equivalent to the above code, but substantially more efficient.
Let's look at an example:
{1,2,3,4,5,6}
Clearly, any positioning of 6 at i will mean the right side of it will be sorted descending and the left side of it ascending. For example, i = 3
{1,2,6,5,4,3}
{1,3,6,5,4,2}
{1,4,6,5,3,2}
...
So for each positioning of N between 2 and n-1, we have (n - 1) choose (position - 1) arrangements. This leads to the answer:
sum [(n - 1) choose (i - 1)], for i = 2...(n - 1)
there are ans perm. and ans is as follows
ans equal to 2^(n-1) and
ans -= 2
as it need to be in between 2 <=i <= n-1 && we know that nC1 ans nCn = 1

How can the complexity of this function be decreased?

I got this function:
def get_sum_slices(a, sum)
count = 0
a.length.times do |n|
a.length.times do |m|
next if n > m
count += 1 if a[n..m].inject(:+) == sum
end
end
count
end
Given this [-2, 0, 3, 2, -7, 4] array and 2 as sum it will return 2 because two sums of a slice equal 0 - [2] and [3, 2, -7, 4]. Anyone an idea on how to improve this to a O(N*log(N))?
I am not familiar with ruby, but it seems to me you are trying to find how many contiguous subarrays that sums to sum.
Your code is doing a brute force of finding ALL subarrays - O(N^2) of those, summing them - O(N) each, and checking if it matches.
This totals in O(N^3) code.
It can be done more efficiently1:
define a new array sums as follows:
sums[i] = arr[0] + arr[1] + ... + arr[i]
It is easy to calculate the above in O(N) time. Note that with the assumption of non negative numbers - this sums array is sorted.
Now, iterate the sums array, and do a binary search for each element sums[i], if there is some index j such that sums[j]-sums[i] == SUM. If the answer is true, add by one (more simple work is needed if array can contains zero, it does not affect complexity).
Since the search is binary search, and is done in O(logN) per iteration, and you do it for each element - you actually have O(NlogN) algorithm.
Similarly, but adding the elements in sums to a hash-set instead of placing them in a sorted array, you can reach O(N) average case performance, since seeking each element is now O(1) on average.
pseudo code:
input: arr , sum
output: numOccurances - number of contiguous subarrays that sums to sum
currSum = 0
S = new hash set (multiset actually)
for each element x in arr:
currSum += x
add x to S
numOccurances= 0
for each element x in S:
let k = number of occurances of sum-x in the hashset
numOccurances += k
return numOccurances
Note that the hash set variant does not need the restriction of non-negative numbers, and can handle it as well.
(1) Assuming your array contains only non negative numbers.
According to amit's algorithm :
def get_sum_slices3(a, sum)
s = a.inject([]) { |m, e| m << e + m.last.to_i }
s.sort!
s.count { |x| s.bsearch { |y| x - y == sum } }
end
Ruby uses quicksort which is nlogn in most cases
You should detail better what you're trying to achieve here. Anyway computing the number of subarray that have a specific sum could be done like this:
def get_sum_slices(a, sum)
count = 0
(2..a.length).each do |n|
a.combination(n).each do |combination|
if combination.inject(:+) == sum
count += 1
puts combination.inspect
end
end
end
count
end
btw your example should return 6
irb> get_sum_slices [-2, 0, 3, 2, -7, 4], 0
[-2, 2]
[-2, 0, 2]
[3, -7, 4]
[0, 3, -7, 4]
[-2, 3, 2, -7, 4]
[-2, 0, 3, 2, -7, 4]
=> 6

N-fold partition of an array with equal sum in each partition

Given an array of integers a, two numbers N and M, return N group of integers from a such that each group sums to M.
For example, say:
a = [1,2,3,4,5]
N = 2
M = 5
Then the algorithm could return [2, 3], [1, 4] or [5], [2, 3] or possibly others.
What algorithms could I use here?
Edit:
I wasn't aware that this problem is NP complete. So maybe it would help if I provided more details on my specific scenario:
So I'm trying to create a "match-up" application. Given the number of teams N and the number of players per team M, the application listens for client requests. Each client request will give a number of players that the client represents. So if I need 2 teams of 5 players, then if 5 clients send requests, each representing 1, 2, 3, 4, 5 players respectively, then my application should generate a match-up between clients [1, 4] and clients [2, 3]. It could also generate a match-up between [1, 4] and [5]; I don't really care.
One implication is that any client representing more than M or less than 0 players is invalid. Hope this could simplify the problem.
this appears to be a variation of the subset sum problem. as this problem is np-complete, there will be no efficient algorithm without further constraints.
note that it is already hard to find a single subset of the original set whose elements would sum up to M.
People give up too easily on NP-complete problems. Just because a problem is NP complete doesn't mean that there aren't more and less efficient algorithms in the general case. That is you can't guarantee that for all inputs there is an answer that can be computed faster than a brute force search, but for many problems you can certainly have methods that are faster than the full search for most inputs.
For this problem there are certainly 'perverse' sets of numbers that will result in worst case search times, because there may be say a large vector of integers, but only one solution and you have to end up trying a very large number of combinations.
But for non-perverse sets, there are probably many solutions, and an efficient way of 'tripping over' a good partitioning will run much faster than NP time.
How you solve this will depend a lot on what you expect to be the more common parameters. It also makes a difference if the integers are all positive, or if negatives are allowed.
In this case I'll assume that:
N is small relative to the length of the vector
All integers are positive.
Integers cannot be re-used.
Algorithm:
Sort the vector, v.
Eliminate elements bigger than M. They can't be part of any solution.
Add up all remaining numbers in v, divide by N. If the result is smaller than M, there is no solution.
Create a new array w, same size as v. For each w[i], sum all the numbers in v[i+1 - end]
So if v was 5 4 3 2 1, w would be 10, 6, 3, 1, 0.
While you have not found enough sets:
Chose the largest number, x, if it is equal to M, emit a solution set with just x, and remove it from the vector, remove the first element from w.
Still not enough sets? (likely), then again while you have not found enough sets:
A solution theory is ([a,b,c], R ) where [a,b,c] is a partial set of elements of v and a remainder R. R = M-sum[a,b,c]. Extending a theory is adding a number to the partial set, and subtracting that number from R. As you extend the theories, if R == 0, that is a possible solution.
Recursively create theories like so: loop over the elements v, as v[i] creating theories, ( [v[i]], R ), And now recursively extend extend each theory from just part of v. Binary search into v to find the first element equal to or smaller than R, v[j]. Start with v[j] and extend each theory with the elements of v from j until R > w[k].
The numbers from v[j] to v[k] are the only numbers that be used to extend a theory and still get R to 0. Numbers larger than v[j] will make R negative. Smaller larger than v[k], and there aren't any more numbers left in the array, even if you used them all to get R to 0
Here is my own Python solution that uses dynamic programming. The algorithm is given here.
def get_subset(lst, s):
'''Given a list of integer `lst` and an integer s, returns
a subset of lst that sums to s, as well as lst minus that subset
'''
q = {}
for i in range(len(lst)):
for j in range(1, s+1):
if lst[i] == j:
q[(i, j)] = (True, [j])
elif i >= 1 and q[(i-1, j)][0]:
q[(i, j)] = (True, q[(i-1, j)][1])
elif i >= 1 and j >= lst[i] and q[(i-1, j-lst[i])][0]:
q[(i, j)] = (True, q[(i-1, j-lst[i])][1] + [lst[i]])
else:
q[(i, j)] = (False, [])
if q[(i, s)][0]:
for k in q[(i, s)][1]:
lst.remove(k)
return q[(i, s)][1], lst
return None, lst
def get_n_subset(n, lst, s):
''' Returns n subsets of lst, each of which sums to s'''
solutions = []
for i in range(n):
sol, lst = get_subset(lst, s)
solutions.append(sol)
return solutions, lst
# print(get_n_subset(7, [1, 2, 3, 4, 5, 7, 8, 4, 1, 2, 3, 1, 1, 1, 2], 5))
# [stdout]: ([[2, 3], [1, 4], [5], [4, 1], [2, 3], [1, 1, 1, 2], None], [7, 8])

Find the middle element in merged arrays in O(logn)

We have two sorted arrays of the same size n. Let's call the array a and b.
How to find the middle element in an sorted array merged by a and b?
Example:
n = 4
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
merged = [1, 2, 3, 3, 4, 4, 5, 6]
mid_element = merged[(0 + merged.length - 1) / 2] = merged[3] = 3
More complicated cases:
Case 1:
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
Case 2:
a = [1, 2, 3, 4, 8]
b = [3, 4, 5, 6, 7]
Case 3:
a = [1, 2, 3, 4, 8]
b = [0, 4, 5, 6, 7]
Case 4:
a = [1, 3, 5, 7]
b = [2, 4, 6, 8]
Time required: O(log n). Any ideas?
Look at the middle of both the arrays. Let's say one value is smaller and the other is bigger.
Discard the lower half of the array with the smaller value. Discard the upper half of the array with the higher value. Now we are left with half of what we started with.
Rinse and repeat until only one element is left in each array. Return the smaller of those two.
If the two middle values are the same, then pick arbitrarily.
Credits: Bill Li's blog
Quite interesting task. I'm not sure about O(logn), but solution O((logn)^2) is obvious for me.
If you know position of some element in first array then you can find how many elements are smaller in both arrays then this value (you know already how many smaller elements are in first array and you can find count of smaller elements in second array using binary search - so just sum up this two numbers). So if you know that number of smaller elements in both arrays is less than N, you should look in to the upper half in first array, otherwise you should move to the lower half. So you will get general binary search with internal binary search. Overall complexity will be O((logn)^2)
Note: if you will not find median in first array then start initial search in the second array. This will not have impact on complexity
So, having
n = 4 and a = [1, 2, 3, 4] and b = [3, 4, 5, 6]
You know the k-th position in result array in advance based on n, which is equal to n.
The result n-th element could be in first array or second.
Let's first assume that element is in first array then
do binary search taking middle element from [l,r], at the beginning l = 0, r = 3;
So taking middle element you know how many elements in the same array smaller, which is middle - 1.
Knowing that middle-1 element is less and knowing you need n-th element you may have [n - (middle-1)]th element from second array to be smaller, greater. If that's greater and previos element is smaller that it's what you need, if it's greater and previous is also greater we need to L = middle, if it's smaller r = middle.
Than do the same for the second array in case you did not find solution for first.
In total log(n) + log(n)

Resources