minimizing the maximum hundai by replacing it with bmw - algorithm

we have given two arrays H[ ] and B[ ] both of size n.
H[i] denotes number of hundai car the (i)th person have.
I have 'm' BMW car,which I can replace with hundai.
B[i] contain number of hundai car equivalent to 1 BMW(means for each person the equivalency may differ).
Given:
H[i]%B[i]=0;
The question is to minimize the max(H[i]) by replacing it with BMW(note that we have only m BMW).
O(n) or O(nlogn) solution required.

Ideas revolving around minimizing .. the maximum of .. are approachable using a binary search I've answered a question along the same lines here: https://stackoverflow.com/a/52679263/10291310
For your case, we can modify the algorithm to,
start = 0, end = 10^18 // Or your max `M` limit
while start <= end:
bmw_used = 0 // Number of bmws used till now for this iteration
mid = (start + end) / 2
// Let's see if its possible to lower down all the hyndais such
// that number of each hundai <= mid
for i in range(0,N):
if H[i] > mid:
// Calculate the number of bmws required to bring H[i] <= mid
bmw_required = ceil(1.0 * (H[i] - mid) / B[i])
bmw_used += bmw_required
// After iterating over the Hyndais
if bmw_used > M:
// We're exceeding the number of bmws, hence increase the number of huyndai
start = mid + 1
else:
// We still have some more bmws left, so let's reduce more hyndais
end = mid - 1
return start
The total runtime complexity of the solution is O(N*log(M)). Cheers!

pseudocode
for k = 1 to m
max_i = 0
for i = 1 to n-1
if H[max_i] < H[i] then max_i = i
if H[max_i] >= B[max_i] then H[max_i] -= B[max_i]
else break
The total complexity is O(mn).
If you can ignore m, it will be O(n).
This algorithm may have some unnecessary loops when there are same H values, but it's not a big problem.

Related

Find continuous subarrays that have at least 1 pair adding up to target sum - Optimization

I took this assessment that had this prompt, and I was able to pass 18/20 tests, but not the last 2 due to hitting the execution time limit. Unfortunately, the input values were not displayed for these tests.
Prompt:
// Given an array of integers **a**, find how many of its continuous subarrays of length **m** that contain at least 1 pair of integers with a sum equal to **k**
Example:
const a = [1,2,3,4,5,6,7];
const m = 5, k = 5;
solution(a, m, k) will yield 2, because there are 2 subarrays in a that have at least 1 pair that add up to k
a[0]...a[4] - [1,2,3,4,5] - 2 + 3 = k ✓
a[1]...a[5] - [2,3,4,5,6] - 2 + 3 = k ✓
a[2]...a[6] - [3,4,5,6,7] - no two elements add up to k ✕
Here was my solution:
// strategy: check each subarray if it contains a two sum pair
// time complexity: O(n * m), where n is the size of a and m is the subarray length
// space complexity: O(m), where m is the subarray length
function solution(a, m, k) {
let count = 0;
for(let i = 0; i <= a.length - m; i++){
let set = new Set();
for(let j = i; j < i + m; j++){
if(set.has(k - a[j])){
count++;
break;
}
else
set.add(a[j]);
}
}
return count;
}
I thought of ways to optimize this algo, but failed to come up with any. Is there any way this can be optimized further for time complexity - perhaps for any edge cases?
Any feedback would be much appreciated!
maintain a map of highest position of the last m values (add/remove/query is O(1)) and highest position of the first value of a complementary pair
for each array element, check if complementary element is in the map, update the highest position if necessary.
if at least m elements were processed and higest position is in the range, increase counter
O(n) overall. Python:
def solution(a, m, k):
count = 0
last_pos = {} # value: last position observed
max_complement_pos = -1
for head, num in enumerate(a, 1): # advance head by one
tail = head - m
# deletion part is to keep space complexity O(m).
# If this is not a concern (likely), safe to omit
if tail > 0 and last_pos[a[tail]] <= tail: # time to pop last element
del last_pos[a[tail]]
max_complement_pos = max(max_complement_pos, last_pos.get(k-num, -1))
count += head >= m and max_complement_pos > tail
last_pos[num] =head # add element at head
return count
Create a counting hash: elt -> count.
When the window moves:
add/increment the new element
decrement the departing element
check if (k - new_elt) is in your hash with a count >= 1. If it is, you've found a good subarray.

How to find a peak in an array?

Given an array such as
[69,20,59,35,10]
I would like to discover all peaks in this array. By the definition of the problem, a peak is an element pi of the array that satisfy the property p_k < p_i > p_j with k < i < j. I'm not interested just in the neighbors of an certain element, I want to analyze all elements before and after this element. With this definition and this example we have the following peaks:
[20,59,35]
[20,59,10]
[20,35,10]
What kind of algorithm or approach I have to use to deal with this?
As mentioned in the comments, the total number of peaks in the worst case would be on the order of O(n^3), therefore an optimal algorithm that outputs all peaks cannot be better than O(n^3) - and the other answers provide cubic-time implementations. An example of an input that has this order of peaks is 00...011...100..0, where each of the three segments of identical characters is of equal length.
However, assuming that you are interested in counting the number of peaks rather than outputting each of them, there is a much faster O(n logn) solution. You can implement a BST (Binary Search Tree) that supports computing ranks (i.e., each node knows how many nodes are to its left - that is, how many values are below it) in logarithmic time. Create two BSTs - one will store the element to the left of the current wannabe peak, and the other to its right. For each i from 1 to n-1, assume it is the middle and find how many pairs of indices would work with it. Every value in the first BST that's lower than the i-th element could be the left index, and every value in the second BST that's lower than the i-th element could be the right index. Hence, the product of these counts is how many peaks with i-th element in the middle exist.
Assuming your arrays are 0-indexed, you can use the algorithm below:
i = 1
while i < length(array) - 1 do
c = array[i]
j = 0
while j < i do
k = i + 1
while k < length(array) do
l = array[j]
r = array[k]
if c > l and c > r then
write('found peak: ', [l, c, r])
k = k + 1
j = j + 1
i = i + 1
I am sharing this figure I drew, in case this can help you to clearly understand what is going on and build a more optimal algorithm.
This is the pseudo code:
Cp is a positive counter: increase it when going uphill
Cn is a negative counter: increase it when going downhill
Reset Cp and Cn when moving horizontally, or when we have reach a valley (Opposite of a Peak).
If array[i] > array[i-1] and array[i] > array[i+1], then array[i] is a peak. The opposite of this statement can be used to find when we reach a valley
After we reach the peak, keep incrementing Cn (Cn += 1) until an eventual reset of Cn.
Right before resetting Cn to zero, set peak_length = Cp+Cn. If we reached the end of the array and no reset is made, then the peak length is Cp+Cn.
Calculate the max of the different peak_length
And Here is the Python code
def peaks_and_valleys(A):
Cp, Cn = 0, 0
longest_path = 0
peak_dict = {} # Track and save the peaks and their length
valley = [] # This is just to track and save the valleys
N = len(A)
for i in range(N-2):
if A[i+1] > A[i]: # Uphill
Cp += 1
if (A[i+1] == A[i+2]): # Uphill and Flat
Cp, Cn = 0, 0
if (A[i+1] > A[i]) and (A[i+1] > A[i+2]): # Peak
peak = A[i+1] # Record and save the peaks
# Keep incrementing negative counter while going Downhill
while i< N-2 and A[i+1] > A[i+2]:
Cn += 1
i += 1
# At the end of the peak, calculate the longest path
longest_path = max(longest_path, Cp+Cn+1)
peak_dict[peak] = longest_path # Track the peaks
elif A[i+1] < A[i]: # Downhill
Cn += 1
if A[i+1] < A[i+2]: # Valley
valley.append(A[i+1]) # Save the Valleys
Cp, Cn = 0, 0
elif (A[i+1] == A[i]) : # Flat
Cp, Cn = 0, 0
print("{Peak': 'Peak Lenght}")
print(peak_dict)
print("valley",valley)
return longest_path

Have O(n^2) algorithm for "two-sum", convert to O(n) linear solution [duplicate]

This question already has answers here:
Find a pair of elements from an array whose sum equals a given number
(33 answers)
Closed 5 years ago.
I have an O(n^2) solution to the classic two-sum problem. Where A[1...n] sorted array of positive integers. t is some positive integer.
Need to show that A contains two distinct elements a and b s.t. a+ b = t
Here is my solution so far:
t = a number;
for (i=0; i<A.length; i++)
for each A[j]
if A[i] + A[j] == t
return true
return false
How do I make this a linear solution? O(n) scratching my head trying to figure it out.
Here's an approach I have in mind so far. i will start at the beginning of A, j will start at the end of A. i will increment, j will decrement. So I'll have two counter variables in the for loop, i & j.
There are couple of ways to improve upon that.
You could extend your algorithm, but instead of doing a simple search for every term, you could do a binary search
t = a number
for (i = 0; i < A.length; i++)
j = binarySearch(A, t - A[i], i, A.length - 1)
if (j != null)
return true
return false
Binary search is done by O(log N) steps, since you perform a binary search per every element in the array, the complexity of the whole algorithm would be O(N*log N)
This already is a tremendous improvement upon O(N^2), but you can do better.
Let's take the sum 11 and the array 1, 3, 4, 8, 9 for example.
You can already see that (3,8) satisfy the sum. To find that, imagine having two pointers, once pointing at the beginning of the array (1), we'll call it H and denote it with bold and another one pointing at the end of the array (9), we'll call it T and denote it with emphasis.
1 3 4 8 9
Right now the sum of the two pointers is 1 + 9 = 10.
10 is less than the desired sum (11), there is no way to reach the desired sum by moving the T pointer, so we'll move the H pointer right:
1 3 4 8 9
3 + 9 = 12 which is greater than the desired sum, there is no way to reach the desired sum by moving the H pointer, moving it right will further increase the sum, moving it left bring us to the initital state, so we'll move the T pointer left:
1 3 4 8 9
3 + 8 = 11 <-- this is the desired sum, we're done.
So the rules of the algorithm consist of moving the H pointer left or moving the T pointer right, we're finished when the sum of the two pointer is equal to the desired sum, or H and T crossed (T became less than H).
t = a number
H = 0
T = A.length - 1
S = -1
while H < T && S != t
S = A[H] + A[T]
if S < t
H++
else if S > t
T--
return S == t
It's easy to see that this algorithm runs at O(N) because we traverse each element at most once.
You make 2 new variables that contain index 0 and index n-1, let's call them i and j respectively.
Then, you check the sum of A[i] and A[j] and if the sum is smaller than t, then increment i (the lower index), and if it is bigger then decrement j (the higher index). continue until you either find i and j such that A[i] + A[j] = t so you return true, or j <= i, and you return false.
int i = 0, j = n-1;
while(i < j) {
if(A[i] + A[j] == t)
return true;
if(A[i] + A[j] < t)
i++;
else
j--;
return false;
Given that A[i] is relatively small (maybe less than 10^6), you can create an array B of size 10^6 with each value equal to 0. Then apply the following algorithm:
for i in 1...N:
B[A[i]] += 1
for i in 1...N:
if t - A[i] > 0:
if B[t-A[i]] > 0:
return True
Edit: well, now that we know that the array is sorted, it may be wiser to find another algorithm. I'll leave the answer here since it still applies to a certain class of related problems.

Specific Max Sum of the elements of an Int array - C/C++

Let's say we have an array: 7 3 1 1 6 13 8 3 3
I have to find the maximum sum of this array such that:
if i add 13 to the sum: i cannot add the neighboring elements from each side: 6 1 and 8 3 cannot be added to the sum
i can skip as many elements as necessary to make the sum max
My algorithm was this:
I take the max element of the array and add that to the sum
I make that element and the neighbor elements -1
I keep doing this until it's not possible to find anymore max
The problem is that for some specific test cases this algorithm is wrong.
Lets see this one: 15 40 45 35
according to my algorithm:
I take 45 and make neighbors -1
The program ends
The correct way to do it is 15 + 35 = 50
This problem can be solved with dynamic programming.
Let A be the array, let DP[m] be the max sum in {A[1]~A[m]}
Every element in A only have two status, been added into the sum or not. First we suppose we have determine DP[1]~DP[m-1], now look at {A[1]~A[m]}, A[m] only have two status that we have said, if A[m] have been added into, A[m-1] and A[m-2] can't be added into the sum, so in add status, the max sum is A[m]+DP[m-3] (intention: DP[m-3] has been the max sum in {A[1]~A[m-3]}), if A[m] have not been added into the sum, the max sum is DP[m-1], so we just need to compare A[m]+DP[m-3] and DP[m-1], the bigger is DP[m]. The thought is the same as mathematical induction.
So the DP equation is DP[m] = max{ DP[m-3]+A[m], DP[m-1] },DP[size(A)] is the result
The complexity is O(n), pseudocode is follow:
DP[1] = A[1];
DP[2] = max(DP[1], DP[2]);
DP[3] = max(DP[1], DP[2], DP[3]);
for(i = 4; i <= size(A); i++) {
DP[i] = DP[i-3] + A[i];
if(DP[i] < DP[i-1])
DP[i] = DP[i-1];
}
It's solvable with a dynamic programming approach, taking O(N) time and O(N) space. Implementation following:
int max_sum(int pos){
if( pos >= N){ // N = array_size
return 0;
}
if( visited(pos) == true ){ // if this state is already checked
return ret[pos]; // ret[i] contains the result for i'th cell
}
ret[pos] = max_sum(pos+3) + A[pos] + ret[pos-2]; // taking this item
ret[pos] = max(ret[pos], ret[pos-1]+ max_sum(pos+1) ); // if skipping this item is better
visited[pos] = true;
return ret[pos];
}
int main(){
// clear the visited array
// and other initializations
cout << max_sum(2) << endl; //for i < 2, ret[i] = A[i]
}
The above problem is max independent set problem (with twist) in a path graph which has dynamic programming solution in O(N).
Recurrence relation for solving it : -
Max(N) = maximum(Max(N-3) + A[N] , Max(N-1))
Explanation:- IF we have to select maximum set from N elements than we can either select Nth element and the maximum set from first N-3 element or we can select maximum from first N-1 elements excluding Nth element.
Pseudo Code : -
Max(1) = A[1];
Max(2) = maximum(A[1],A[2]);
Max(3) = maximum(A[3],Max(2));
for(i=4;i<=N;i++) {
Max(N) = maximum(Max(N-3)+A[N],Max(N-1));
}
As suggested, this is a dynamic programming problem.
First, some notation, Let:
A be the array, of integers, of length N
A[a..b) be the subset of A containing the elements at index a up to
but not including b (the half open interval).
M be an array such that M[k] is the specific max sum of A[0..k)
such that M[N] is the answer to our original problem.
We can describe an element of M (M[n]) by its relation to one or more elements of M (M[k]) where k < n. And this lends itself to a nice linear time algorithm. So what is this relationship?
The base cases are as follows:
M[0] is the max specific sum of the empty list, which must be 0.
M[1] is the max specific sum for a single element, so must be
that element: A[0].
M[2] is the max specific sum of the first two elements. With only
two elements, we can either pick the first or the second, so we better
pick the larger of the two: max(A[0], A[1]).
Now, how do we calculate M[n] if we know M[0..n)? Well, we have a choice to make:
Either we add A[n-1] (the last element in A[0..n)) or we don't. We don't know for
certain whether adding A[n-1] in will make for a larger sum, so we try both and take
the max:
If we don't add A[n-1] what would the sum be? It would be the same as the
max specific sum immediately before it: M[n-1].
If we do add A[n-1] then we can't have the previous two elements in our
solution, but we can have any elements before those. We know that M[n-1] and
M[n-2] might have used those previous two elements, but M[n-3] definitely
didn't, because it is the max in the range A[0..n-3). So we get
M[n-3] + A[n-1].
We don't know which one is bigger though, (M[n-1] or M[n-3] + A[n-1]), so to find
the max specific sum at M[n] we must take the max of those two.
So the relation becomes:
M[0] = 0
M[1] = A[0]
M[2] = max {A[0], A[1]}
M[n] = max {M[n-1], M[n-3] + A[n-1]} where n > 2
Note a lot of answers seem to ignore the case for the empty list, but it is
definitely a valid input, so should be accounted for.
The simple translation of the solution in C++ is as follows:
(Take special note of the fact that the size of m is one bigger than the size of a)
int max_specific_sum(std::vector<int> a)
{
std::vector<int> m( a.size() + 1 );
m[0] = 0; m[1] = a[0]; m[2] = std::max(a[0], a[1]);
for(unsigned int i = 3; i <= a.size(); ++i)
m[i] = std::max(m[i-1], m[i-3] + a[i-1]);
return m.back();
}
BUT This implementation has a linear space requirement in the size of A. If you look at the definition of M[n], you will see that it only relies on M[n-1] and M[n-3] (and not the whole preceding list of elements), and this means you need only store the previous 3 elements in M, resulting in a constant space requirement. (The details of this implementation are left to the OP).

Maximum Weight Increasing Subsequence

In the Longest Increasing Subsequence Problem if we change the length by weight i.e the length of each element Ai is 1 if we change it to Wi
How can we do it in O(NlogN).
For Example
For an array of 8 Elements
Elements 1 2 3 4 1 2 3 4
Weights 10 20 30 40 15 15 15 50
The maximum weight is 110.
I found the LIS solution on wikipedia but I can't modify it to solve this problem.
Still, we use f[i] denotes the max value we can get with a sequence end with E[i].
So generally we have for (int i = 1;i <= n;i++) f[i] = dp(i); and initially f[0] = 0; and E[0] = -INF;
Now we shall calculate f[i] in dp(i) within O(log(N)).
in dp(i), we shall find the max f[j] with E[j] < E[i] for all 0 <= j < i. Here we can maintain a Segment Tree.
So dp(i) = find_max(1,E[i]-1) + W[i](this takes O(log)), and for every f[i] already calculated, update(E[i],f[i]).
So the whole algorithm takes (O(NlogN)).
Tip: If E[i] varies in a very big range, it can be Discretizationed.
Here is pure recursion implementation in swift:
// input is Array of (a,w), where a is element and w is weight
func lisw(input: [(Int, Int)], index:Int = 0, largestA:Int? = nil)->Int{
guard index < input.count else { return 0 }
let (a,w) = input[index]
if a <= largestA {
return lisw(input: input, index: index + 1, largestA: largestA)
}
let without_me = lisw(input: input, index: index + 1, largestA: largestA == nil ? a : largestA)
let with_me = lisw(input: input, index: index + 1, largestA: a) + w
return max(without_me,with_me)
}
Feel free to add memoization ;)

Resources