I have an array A of size N. All elements are positive integers. In one step, I can add two adjacent elements and replace them with their sum. That said, the array size reduces by 1. Now I need to make all the elements same by performing minimum number of steps.
For example: A = [1,2,3,2,1,3].
Step 1: Merge index 0 and 1 ==> A = [3,3,2,1,3]
Step 2: Merge index 2 and 3 (of new array) ==> [3,3,3,3]
Hence number of steps are 2.
I couldn't think of a straight solution, so tried a recursive approach by merging all indices one by one and returning the min level I can get when either array size is 1 or all elements are equal.
Below is the code I tried:
# Checks if all the elements are same or not
def check(A):
if len(set(A)) == 1:
return True
return False
# Recursive function to find min steps
def min_steps(N,A,level):
# If all are equal return the level
if N == 1 or check(A):
return level
# Initialize min variable
mn = float('+inf')
# Try merging all one by one and recur
for i in range(N-1):
temp = A[:]
temp[i]+=temp[i+1]
temp.pop(i+1)
mn = min(mn, min_steps(N-1,temp, level+1))
return mn
This solution has complexity of O(N^N). I want to reduce it to polynomial time near to O(N^2) or O(N^3). Can anyone help me modify this solution or tell me if I am missing something?
Combining any k adjacent pairs of elements (even if they include elements formed from previous combining steps) leaves exactly n-k elements in total, each of which we can map back to the contiguous subarray of the original problem that constitutes the elements that were added together to form it. So, this problem is equivalent to partitioning the array into the largest possible number of contiguous subarrays such that all subarrays have the same sum: Any adjacent pair of elements within the same subarray can be combined into a single element, and this process repeated within the subarray with adjacent pairs chosen in any order, until all elements have been combined into a single element.
So, if there are n elements and they sum to T, then a simple O(nT) algorithm is:
For i from 0 to T:
Try partitioning the elements into subarrays each having sum i. This amounts to scanning along the array, greedily adding the current element to the current subarray if the sum of elements in the current subarray is strictly < i. When we reach a total of exactly i, the current subarray ends and a new subarray (initially having sum 0) begins. If adding the current element takes us above the target of i, or if we run out of elements before reaching the target, stop this scan and try the next outer loop iteration (value of i). OTOH if we get to the end, having formed k subarrays in the process, stop and report n-k as the optimal (minimum possible) number of combining moves.
A small speedup would be to only try target i values that evenly divide T.
EDIT: To improve the time complexity from O(nT) to O(n^2), it suffices to only try target i values corresponding to sums of prefixes of the array (since there must be a subarray containing the first element, and this subarray can only have such a sum).
Related
For example, given an array
a = [1, 2, 3, 7, 8, 9]
and an integer
i = 2. Find maximal subarrays where the distance between the largest and the smallest elements is at most i. The output for the example above would be:
[1,2,3] [7,8,9]
The subarrays are maximal in the sense given two subarrays A and B. There exists no element b in B such that A + b satisfies the condition given. Does there exist a non-polynomial time algorithm for said problem ?
This problem might be solved in linear time using method of two pointers and two deques storing indices, the first deque keeps minimum, another keeps maximum in sliding window.
Deque for minimum (similar for maximum):
current_minimum = a[minq.front]
Adding i-th element of array: //at the right index
while (!minq.empty and a[minq.back] > a[i]):
//last element has no chance to become a minimum because newer one is better
minq.pop_back
minq.push_back(i)
Extracting j-th element: //at the left index
if (!minq.empty and minq.front == j)
minq.pop_front
So min-deque always contains non-decreasing sequence.
Now set left and right indices in 0, insert index 0 into deques, and start to move right. At every step add index in order into deques, and check than left..right interval range is good. When range becomes too wide (min-max distance is exceeded), stop moving right index, check length of the last good interval, compare with the best length.
Now move left index, removing elements from deques. When max-min becomes good, stop left and start with right again. Repeat until array end.
I have 2 sorted integer lists or arrays a and b, both having same number of elements. I want to pair an element in a with an element in b such that when I take smaller element in all pairs, their sum is minimum.
For example,
a=[1,7,14,18]
b=[8,9,10,12]
I would be pairing [(1,12),(7,10),(14,9),(18,8)] and then taking smaller element in each pair, namely, [1,7,9,8], I will get minimum sum. This is just one possibility I took. I want to know if this method of pairing elements of first list from the first element and moving forward with elements of the second list starting from end and going backwards will give me the minimum sum.
Yes, that method of pairing the largest with the smallest will work:
If the largest element in the second array is smaller than the smallest in the first array, any pairing method will work and so pair the remaining elements using your method.
If not, pairing the first pair with your method will ensure the smallest from the first (which should be counted) will be counted and the largest from the second (which should not be counted) will not be counted
Repeat steps 1 and 2 with the remaining elements from both arrays until you run out of elements
As you can see, the smallest remaining elements from the arrays will always be counted at each step along the way, and so the sum of the smallest from the resulting pairs will be minimized as desired.
I have a mathematical/algorithmic problem here.
Given an array of numbers, find a way to separate it to 5 subarrays, so that sum of each subarrays is less than or equal to a given number. All numbers from the initial array, must go to one of the subarrays, and be part of one sum.
So the input to the algorithm would be:
d - representing the number that each subarrays sum has to be less or equal
A - representing the array of numbers that will be separated to different subarrays, and will be part of one sum
Algorithm complexity must be polynomial.
Thank you.
If by "subarray" you mean "subset" as opposed to "contiguous slice", it is impossible to find a polynomial time algorithm for this problem (unless P = NP). The Partition Problem is to partition a list of numbers into to sets such that the sum of both sets are equal. It is known to be NP-complete. The partition problem can be reduced to your problem as follows:
Suppose that x1, ..., x_n are positive numbers that you want to partition into 2 sets such that their sums are equal. Let d be this common sum (which would be the sum of the xi divided by 2). extend x_i to an array, A, of size n+3 by adding three copies of d. Clearly the only way to partition A into 5 subarrays so that the sum of each is less than or equal to d is if the sum of each actually equals d. This would in turn require 3 of the subarrays to have length 1, each consisting of the number d. The remaining 2 subarrays would be exactly a partition of the original n numbers.
On the other hand, if there are additional constraints on what the numbers are and/or the subarrays need to be, there might be a polynomial solution. But, if so, you should clearly spell out what there constraints are.
Set up of the problem:
d : the upper bound for the subarray
A : the initial array
Assuming A is not sorted.
(Heuristic)
Algorithm:
1.Sort A in ascending order using standard sorting algorithm->O(nlogn)
2.Check if the largest element of A is greater than d ->(constant)
if yes, no solution
if no, continue
3.Sum up all the element in A, denote S. Check if S/5 > d ->O(n)
if yes, no solution
if no, continue
4.Using greedy approach, create a new subarray Asi, add next biggest element aj in the sorted A to Asi so that the sum of Asi does not exceed d. Remove aj from sorted A ->O(n)
repeat step4 until either of the condition satisfied:
I.At creating subarray Asi, there are only 5-i element left
In this case, split the remaining element to individual subarray, done
II. i = 5. There are 5 subarray created.
The algorithm described above is bounded by O(nlogn) therefore in polynomial time.
I'm trying to implement this in my program, namely we have given array of K pairs, where each pair is in form (i,j) and i<=N, j<=M, N,M<=1000, and K<=N*M.
Now for each pair we want to count pairs in which the first element is strictly bigger and the second one is strictly less, for example if our pair is: (2,3), we want to count the pair (4,1), but not the pair (1,2) because 1 is less than 2.
Is this possible to do in O(N*M) time complexity?
Description
Given an Array of size (n*k+b) where n elements occur k times and one element occurs b times, in other words there are n+1 distinct Elements. Given that 0 < b < k find the element occurring b times.
My Attempted solutions
Obvious solution will be using hashing but it will not work if the numbers are very large. Complexity is O(n)
Using map to store the frequencies of each element and then traversing map to find the element occurring b times.As Map's are implemented as height balanced trees Complexity will be O(nlogn).
Both of my solution were accepted but the interviewer wanted a linear solution without using hashing and hint he gave was make the height of tree constant in tree in which you are storing frequencies, but I am not able to figure out the correct solution yet.
I want to know how to solve this problem in linear time without hashing?
EDIT:
Sample:
Input: n=2 b=2 k=3
Aarray: 2 2 2 3 3 3 1 1
Output: 1
I assume:
The elements of the array are comparable.
We know the values of n and k beforehand.
A solution O(n*k+b) is good enough.
Let the number occuring only b times be S. We are trying to find the S in an array of n*k+b size.
Recursive Step: Find the median element of the current array slice as in Quick Sort in lineer time. Let the median element be M.
After the recursive step you have an array where all elements smaller than M occur on the left of the first occurence of M. All M elements are next to each other and all element larger than M are on the right of all occurences of M.
Look at the index of the leftmost M and calculate whether S<M or S>=M. Recurse either on the left slice or the right slice.
So you are doing a quick sort but delving only one part of the divisions at any time. You will recurse O(logN) times but each time with 1/2, 1/4, 1/8, .. sizes of the original array, so the total time will still be O(n).
Clarification: Let's say n=20 and k = 10. Then, there are 21 distinct elements in the array, 20 of which occur 10 times and the last occur let's say 7 times. I find the medium element, let's say it is 1111. If the S<1111 than the index of the leftmost occurence of 1111 will be less than 11*10. If S>=1111 then the index will be equal to 11*10.
Full example: n = 4. k = 3. Array = {1,2,3,4,5,1,2,3,4,5,1,2,3,5}
After the first recursive step I find the median element is 3 and the array is something like: {1,2,1,2,1,2,3,3,3,5,4,5,5,4} There are 6 elements on the left of 3. 6 is a multiple of k=3. So each element must be occuring 3 times there. So S>=3. Recurse on the right side. And so on.
An idea using cyclic groups.
To guess i-th bit of answer, follow this procedure:
Count how many numbers in array has i-th bit set, store as cnt
If cnt % k is non-zero, then i-th bit of answer is set. Otherwise it is clear.
To guess whole number, repeat the above for every bit.
This solution is technically O((n*k+b)*log max N), where max N is maximal value in the table, but because number of bits is usually constant, this solution is linear in array size.
No hashing, memory usage is O(log k * log max N).
Example implementation:
from random import randint, shuffle
def generate_test_data(n, k, b):
k_rep = [randint(0, 1000) for i in xrange(n)]
b_rep = [randint(0, 1000)]
numbers = k_rep*k + b_rep*b
shuffle(numbers)
print "k_rep: ", k_rep
print "b_rep: ", b_rep
return numbers
def solve(data, k):
cnts = [0]*10
for number in data:
bits = [number >> b & 1 for b in xrange(10)]
cnts = [cnts[i] + bits[i] for i in xrange(10)]
return reduce(lambda a,b:2*a+(b%k>0), reversed(cnts), 0)
print "Answer: ", solve(generate_test_data(10, 15, 13), 3)
In order to have a constant height B-tree containing n distinct elements, with height h constant, you need z=n^(1/h) children per nodes: h=log_z(n), thus h=log(n)/log(z), thus log(z)=log(n)/h, thus z=e^(log(n)/h), thus z=n^(1/h).
Example, with n=1000000, h=10, z=3.98, that is z=4.
The time to reach a node in that case is O(h.log(z)). Assuming h and z to be "constant" (since N=n.k, then log(z)=log(n^(1/h))=log(N/k^(1/h))=ct by properly choosing h based on k, you can then say that O(h.log(z))=O(1)... This is a bit far-fetched, but maybe that was the kind of thing the interviewer wanted to hear?
UPDATE: this one use hashing, so it's not a good answer :(
in python this would be linear time (set will remove the duplicates):
result = (sum(set(arr))*k - sum(arr)) / (k - b)
If 'k' is even and 'b' is odd, then XOR will do. :)