You are given an integer array a of size n and an integer m. you have to distribute the elements of array A into M groups such that the maximum sum of elements in each group is minimum and the elements of array A allocated to any group is contiguous. Write a program to determine the maximum sum of elements among all the groups.
The brute force could be having all the M possible sub-arrays from the given array and checking with them. But this would take a time complexity of O(n^3).
Here I used dynamic programming which would take both the time and space complexity of O(n*M).
Try all the possible partitions that are possible using recursion. Let dp[ind][k] be the minimum of maximum sums till index 'ind' with 'k' partitions. Hence the possible states will be partition at every index from the index ind till n. The minimum of maximum sums of all those states will be our answer.
In order not to recur for visited element, we can use memoize.
Below is the implementation of the above approach:
from sys import maxsize
from sys import setrecursionlimit
setrecursionlimit(10**6)
def max_sum_groups_min(arr, n, ind, M, dp):
# If M segments have been divided
if M == -1:
# If we are at the end
if ind == n:
return -maxsize
# If we do not reach end, return a maximum number
# that cannot be a min value than existing answer
return maxsize
# If at the end, but M segmments are not formed
if ind == n:
return maxsize
# If it's already visited: Memoization
if dp[ind][M] != False:
return dp[ind][M]
ans = maxsize
cur = 0
# Iterate and try to break at every segment and create a segment
for i in range(ind, n):
# sum of elements in current segment
cur += arr[i]
# Find the maximum of all segments after adding and
# find minimum in all possible combinations
ans = min(ans, max(max_sum_groups_min(arr, n, i + 1, M - 1, dp), cur))
# Return the answer by memoizing it
dp[ind][M] = ans
return ans
n = int(input("Enter array size: "))
arr = list(map(int, input("Enter list of elements: ").split()))
group = int(input("Enter the group size: "))
# keeping 'False' inorder to keep track it's not visited
dp = [[False for _ in range(group)] for _ in range(n)]
print(max_sum_groups_min(arr, n, 0, group - 1, dp))
For eg: n = 8, arr = [1, 2, 3, 4, 5, 6, 7, 8], M = 4
Here the given array can be divided into 4 groups as:
{1, 2, 3, 4}, {5, 6}, {7}, {8}
in which 2nd group has largest sum(11) among all the groups which is minimum of all possibilities.
Thanks,
Jayanth.
Related
I am going through this program mentioned here.
Given an array arr[] of N integers. The task is to count the total number of subarrays of the given array such that the difference between the consecutive elements in the subarrays is one. That is, for any index i in the subarrays, arr[i+1] – arr[i] = 1.
Examples:
Input : arr[] = {1, 2, 3}
Output : 3
The subarrays are {1, 2}. {2, 3} and {1, 2, 3}
Input : arr[] = {1, 2, 3, 5, 6, 7}
Output : 6
Efficient Approach: An efficient approach is to observe that in an array of length say K, the total number of subarrays of size greater than 1 = (K)*(K-1)/2.
So, the idea is to traverse the array by using two pointers to calculate subarrays with consecutive elements in a window of maximum length and then calculate all subarrays in that window using the above formula.
Below is the step-by-step algorithm:
Take two pointers to say fast and slow, for maintaining a window of consecutive elements.
Start traversing the array.
If elements differ by 1 increment only the fast pointer.
Else, calculate the length of the current window between the indexes fast and slow.
My question is about the statement An efficient approach is to observe that in an array of length say K, the total number of subarrays of size greater than 1 = (K)*(K-1)/2 How this formula (K)*(K-1)/2 is derived?
The number of subarrays of size 1 is K.
The number of subarrays of size 2 is K-1.
( We need to select subarrays of size 2, hence we can have pair with the indices (0,1), (1,2), .......(K-1,K). In total we can have K-1 such pairs)
The number of subarrays of size 3 is K-2.
...
...
The number of subarrays of size K is 1
So, the number of subarrays of size greater than 1 is
= K-1 + K-2 + K-3+ ...... 1
= (K + K-1 + K-2 + K-3+ ...... 1) - K //adding and removing K
= (K(K+1)/2) - K
= K(K-1)/2
We have an integer array A[] of size N (1 ≤ N ≤ 10^4), which originally is a sorted array with entries 1...N. For any permutation P of size N, the array is shuffled so that i-th entry from the left before the shuffle is at the Ai-th position after the shuffle. You would keep repeating this shuffle until the array is sorted again.
For example, for A[] = {1, 2, 3, 4}, if P = {1, 2, 3, 4}, it would only take one move for the array to be sorted (the entries would move to their original positions). If P = {4, 3, 1, 2}, then it would take 4 moves for the array to be sorted again:
Move 0 | [1, 2, 3, 4]
Move 1 | [3, 4, 2, 1]
Move 2 | [2, 1, 4, 3]
Move 3 | [4, 3, 1, 2]
Move 4 | [1, 2, 3, 4]
The problem is to find the sum of all positive integers J for which you can generate a permutation that requires J moves to get the array sorted again.
Example:
For A[] = {1, 2, 3, 4}, you can generate permutations that require 1, 2, 3, and 4 steps:
Requires 1 move: P = {1, 2, 3, 4}
Requires 2 moves: P = {1, 3, 2, 4}
Requires 3 moves: P = {1, 4, 2, 3}
Requires 4 moves: P = {4, 3, 1, 2}
So you would output 1 + 2 + 3 + 4 = 10.
Some observations I have made is that you can always generate a permutation that requires J moves for (1 ≤ J < N). This is because in the permutation, you would simply shift by 1 all the entries in the range of size J. However, for permutations that requires J moves where J ≥ N, you would need another algorithm.
The brute-force solution would be checking every permutation, or N! permutations which definitely wouldn't fit in run time. I'm looking for an algorithm with run time at most O(N^2).
EDIT 1: A permutation that requires N moves will always be guaranteed as well, as you can create a permutation where every entry is misplaced, and not just swapped with another entry. The question becomes how to find permutations where J > N.
EDIT 2: #ljeabmreosn made the observation that there exists a permutation that takes J steps if and only if there are natural numbers a_1 + ... + a_k = N and LCM(a_1, ..., a_k) = J. So using that observation, the problem comes down to finding all partitions of the array, or partitions of the integer N. However, this won't be a quadratic algorithm - how can I find them efficiently?
Sum of distinct orders of degree-n permutations.
https://oeis.org/A060179
This is the number you are looking for, with a formula, and some maple code.
As often when trying to compute an integer sequence, compute the first few values (here 1, 1, 3, 6, 10, 21) and look for it in the great "On-line Encyclopedia of Integer Sequences".
Here is some python code inspired by it, I think it fits your complexity goals.
def primes_upto(limit):
is_prime = [False] * 2 + [True] * (limit - 1)
for n in range(int(limit**0.5 + 1.5)):
if is_prime[n]:
for i in range(n*n, limit+1, n):
is_prime[i] = False
return [i for i, prime in enumerate(is_prime) if prime]
def sum_of_distinct_order_of_Sn(N):
primes = primes_upto(N)
res = [1]*(N+1)
for p in primes:
for n in range(N,p-1,-1):
pj = p
while pj <= n:
res[n] += res[n-pj] * pj
pj *= p
return res[N]
on my machine:
>%time sum_of_distinct_order_of_Sn(10000)
CPU times: user 2.2 s, sys: 7.54 ms, total: 2.21 s
Wall time: 2.21 s
51341741532026057701809813988399192987996798390239678614311608467285998981748581403905219380703280665170264840434783302693471342230109536512960230
Basically, we are given a number N and K, we need to find an array of size K such that the product of the array elements is N with the maximum of the elements being minimized.
for eg:
420 3
ans: 6 7 10
explanation: 420 can be written as the product of 6,10 and 7. Also it can be written as 5 7 12 but 10(maximum of 6 10 and 7) is minimum than 12(maximum of 5 7 12).
Constraints: numbers>0; 0 <= N < 10^6; 1<=k<=100
What I did so far was to first find the prime factors but after that I can't think of an efficient way to get the sequence.
Basically, amritanshu had a pretty good idea: You have a list of the prime factors and split this list into a list containing the K biggest factors and another containing the other prime factors:
[2, 2], [3, 5, 7]
Then you multiply the biggest element of the first list with the smallest element of the second list and overwrite the element of the second list with the result. Remove the biggest element of the first list. Repeat these steps until your first list is empty:
[2, 2], [3, 5, 7]
[2], [6, 5, 7] // 5 is now the smallest element
[], [6, 10, 7]
here another example:
N = 2310 = 2 * 3 * 5 * 7 * 11
K = 3
[2, 3], [5, 7, 11]
[2], [15, 7, 11]
[], [15, 14, 11]
however, this algorithm is still not the perfect one for some cases like N = 2310, K = 2:
[2, 3, 5], [7, 11]
[2, 3], [35, 11]
[2], [35, 33]
[], [35, 66] // better: [], [42, 55]
So, I thought you actually want to split the factors such that the factors are as close as possible to the Kth root of N. So I come up with this algorithm:
calculate R, the smallest integer bigger than or equal to the Kth root of N
calculate the gcd of R and N
if the gcd is equal to R, add R to the list, call your algorithm recursively with N / R, K-1, add the result to the list and return the list
if the gcd is not equal to R, add it to R and go to step 2
here is a little bit of python code:
import math
def gcd(a, b):
while b:
a, b = b, a % b
return a
def root(N, K):
R = int(math.exp(math.log(N) / K))
if R ** K < N:
R += 1
return R
def find_factors(N, K):
if K == 1:
return [N]
R = root(N, K)
while True:
GCD = gcd(N, R)
if GCD == R:
return [R] + find_factors(N // R, K-1)
R += GCD
EDIT:
I just noticed that this algorithm is still giving incorrect results in many cases. The correct way is incrementing R until it divides N:
def find_factors(N, K):
if K == 1:
return [N]
R = root(N, K)
while True:
if N % R == 0:
return [R] + find_factors(N // R, K-1)
R += 1
This way you don't need gcd.
Overall, I guess you need to factorize N and then essentially make some brute-force approach trying to combine the prime factors into combined factors of roughly equal size. Generally, that should not be too bad, because factorizing is already the most expensive part in many cases.
Original answer (wrong) (see comment by #gus):
Without proof of correctness, assuming N>0, K>0, in pseudo code:
Factorize N into prime factors, store into array F
find smallest integer m>=0 such that length(F) <= 2^m*K
Fill F by 1s to get size 2^m*K.
For i=m down to 1
sort F
for j=1 to 2^(i-1)*K
F[j] = F[j] * F[2^i*K+1-j] (multiply smallest with largest, and so on)
F=F[1:2^(i-1)*K] (delete upper half of F)
F contains result.
Example 420 3:
F={2,2,3,5,7}
m=1
F={1,2,2,3,5,7}
F={7,10,6} DONE
Example 2310 2:
F={2,3,5,7,11}
m=2
F={1,1,1,2,3,5,7,11} (fill to 2^m*K and sort)
F={11,7,5,6} (reduce to half)
F={5,6,7,11} (sort)
F={55, 42} DONE
Example N=17^3*72, K=3
F={2,2,2,3,3,17,17,17}
m=2
F={1,1,1,1,2,2,2,3,3,17,17,17}
F={17,17,17,3,6,4}
F={3,4,6,17,17,17}
F={3,4,6,17,17,17}
F={51,68,102}
We have an array of N positive elements. We can perform M operations on this array. In each operation we have to select a subarray(contiguous) of length W and increase each element by 1. Each element of the array can be increased at most K times.
We have to perform these operations such that the minimum element in the array is maximized.
1 <= N, W <= 10^5
1 <= M, K <= 10^5
Time limit: 1 sec
I can think of an O(n^2) solution but it is exceeding time limit. Can somebody provide an O(nlogn) or better solution for this?
P.S.- This is an interview question
It was asked in a Google interview and I solved it by using sliding window, heap and increment in a range logic. I will solve the problem in 3 parts:
Finding out the minimum of every subarray of size W. This can be done in O(n) by using sliding window with priority queue. The maximum of every window must be inserted into a min-heap with 3 variable: [array_value, left_index, right_index]
Now, make auxiliary array initialised to 0 with of size N. Perform pop operation on heap M number of times and in each pop operation perform 3 task:
value, left_index, right_index = heap.pop() # theoretical function to pop minimum
Increment the value by 1,
increment by 1 in auxiliary array at left_index and decrement by 1 in
auxiliary array at right_index+1
Again insert this pair into heap. [with incremented value and same indexes]
After performing M operations traverse the given array with auxiliary array and add the cumulative sum till index 'i' to element at index 'i' in array.
Return minimum of array.
Time Complexity
O(N) <- for minimum element in every window + building heap.
O(M*logN) <- Extracting and inserting into heap.
O(N) <- For traversing to add cumulative sum.
So, overall is O(N + M*logN + N) which is O(M*logN)
Space Complexity
O(N) <- Extra array + heap.
Few things can be easily optimised above like inserting values in heap, only left_index can be inserted and as right_index = left_index + k.
My Code
from heapq import heappop, heappush
from collections import deque
def find_maximised_minimum(arr, n, m, k):
"""
arr -> Array, n-> Size of array
m -> increment operation that can be performed
k -> window size
"""
heap = []
q = deque()
# sliding window + heap building
for i in range(k):
while q and arr[q[-1]] > arr[i]:
q.pop()
q.append(i)
for i in range(k, n):
heappush(heap, [arr[q[0]], i - k, i - 1])
while q and q[0] <= i - k:
q.popleft()
while q and arr[q[-1]] > arr[i]:
q.pop()
q.append(i)
heappush(heap, [arr[q[0]], n - k, n - 1])
# auxiliary array
temp = [0 for i in range(n)]
# performing M increment operations
while m:
top = heappop(heap)
temp[top[1]] += 1
try:
temp[top[2] + 1] -= 1
except:
# when the index is last, so just ignore
pass
top[0] += 1
heappush(heap, top)
m -= 1
# finding cumulative sum
sumi = 0
for i in range(n):
sumi += temp[i]
arr[i] += sumi
print(min(arr))
if __name__ == '__main__':
# find([1, 2, 3, 4, 5, 6], 6, 5, 2)
# find([73, 77, 60, 100, 94, 24, 31], 7, 9, 1)
# find([24, 41, 100, 70, 97, 89, 38, 68, 41, 93], 10, 6, 5)
# find([88, 36, 72, 72, 37, 76, 83, 18, 76, 54], 10, 4, 3)
find_maximised_minimum([98, 97, 23, 13, 27, 100, 75, 42], 8, 5, 1)
What if we kept a copy of the array sorted ascending, pointing each element to its original index? Think about the order of priority when incrementing the elements. Also, does the final order of operations matter?
Once the lowest element reaches the next lowest element, what must then be incremented? And if we apply k operations to any one element does it matter in which w those increments were applied?
I'm building a decision tree algorithm. The sorting is very expensive in this algorithm because for every split I need to sort each column. So at the beginning - even before tree construction I'm presorting variables - I'm creating a matrix so for each column in the matrix I save its ranking. Then when I want to sort the variable in some split I don't actually sort it but use the presorted ranking array. The problem is that I don't know how to do it in a space efficient manner.
A naive solution of this is below. This is only for 1 variabe (v) and 1 split (split_ind).
import numpy as np
v = np.array([60,70,50,10,20,0,90,80,30,40])
sortperm = v.argsort() #1 sortperm = array([5, 3, 4, 8, 9, 2, 0, 1, 7, 6])
rankperm = sortperm.argsort() #2 rankperm = array([6, 7, 5, 1, 2, 0, 9, 8, 3, 4])
split_ind = np.array([3,6,4,8,9]) # this is my split (random)
# split v and sortperm
v_split = v[split_ind] # v_split = array([10, 90, 20, 30, 40])
rankperm_split = rankperm[split_ind] # rankperm_split = array([1, 9, 2, 3, 4])
vsorted_dummy = np.ones(10)*-1 #3 allocate "empty" array[N]
vsorted_dummy[rankperm_split] = v_split
vsorted = vsorted_dummy[vsorted_dummy!=-1] # vsorted = array([ 10., 20., 30., 40., 90.])
Basically I have 2 questions:
Is double sorting necessary to create ranking array? (#1 and #2)
In the line #3 I'm allocating array[N]. This is very inefficent in terms of space because even if split size n << N I have to allocate whole array. The problem here is how to calculate rankperm_split. In the example original rankperm_split = [1,9,2,3,4] while it should be really [1,5,2,3,4]. This problem can be reformulated so that I want to create a "dense" integer array that has maximum gap of 1 and it keeps the ranking of the array intact.
UPDATE
I think that second point is the key here. This problem can be redefined as
A[N] - array of size N
B[N] - array of size N
I want to transform array A to array B so that:
Ranking of the elements stays the same (for each pair i,j if A[i] < A[j] then B[i] < B[j]
Array B has only elements from 1 to N where each element is unique.
A few examples of this transformation:
[3,4,5] => [1,2,3]
[30,40,50] => [1,2,3]
[30,50,40] => [1,3,2]
[3,4,50] => [1,2,3]
A naive implementation (with sorting) can be defined like this (in Python)
def remap(a):
a_ = sorted(a)
b = [a_.index(e)+1 for e in a]
return b