Reputation: 131
I recently heard this question from a friend who was asked this in an interview. He was not able to figure it out and i have not yet found any efficient solution to it either. I hope there is an algorithmist here who can show me a new approach
Question:
Given an array A and a number S', provide an efficient algorithm (nlogn) to find a number K such that if all elements in A greater than K are changed to K, the sum of all elements in the resulting array will be S'.
Example, given A: [90,30,100,40,20]
and S' = 210
, K
will be 60
.
Upvotes: 13
Views: 3742
Reputation: 362087
Written in Python, which should be quite readable even if you don't know the language:
#!/usr/bin/env python
A = [90, 30, 100, 40, 20]
S = 210
K = 60
A = sorted(A)
prev = 0
sum = 0
for index, value in enumerate(A):
# What do we need to set all subsequent values to to get the desired sum?
solution = (S - sum) / (len(A) - index)
# That answer can't be too big or too small.
if prev < solution <= value:
print solution
sum += value
prev = value
Result:
60
Sorting is O(n log n) and the loop is O(n). Combined the algorithm as a whole is therefore O(n log n).
Upvotes: 15
Reputation: 412
Well it looks like I am late but anyway hope this algorithm makes some sense .
In the end N/P should give you the perfect answer and it's in O(N) Time complexity and O(1) Space Complexity.
Find the executing code here : http://ideone.com/MDL3iy
import java.util.Scanner;
class Ideone {
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
int S = in.nextInt();
int[] array = {90,30,100,40,20};
int len = array.length;
int sAvg = S/len;
int trackSmallerThanAverage = 0;
int countMorethanAverage = 0;
for(int i=0; i<len; i++) {
if(array[i] > sAvg) {
countMorethanAverage ++;
} else if (array[i]<sAvg) {
trackSmallerThanAverage += array[i];
}
}
int finalValue = ( S - trackSmallerThanAverage )/countMorethanAverage;
System.out.println(finalValue);
in.close();
}
}
Upvotes: 2
Reputation: 3890
sort it nlogn
int[] sortedArray;
int s;
int sum=0;
for(int i=0; i<sortedArray.length; i++){
sum = sum + sortedArray[i];
if((s - sum)/(sortedArray.length - i) < sortedArray[i+1]){
return (s - sum)/(sortedArray.length - i);
}
}
Upvotes: 0
Reputation: 23643
This can be accomplished without sorting in O(n) time by using a variation on linear time select as follows (note that the running times of the iterations of the while loop form a geometric series -- the partition subroutine splits a range of an array, from lower to upper, into elements less than or greater than the element of rank mid, and the running time is proportional to the size of the range of the array):
foo(x, s) {
sumBelow = 0;
lower = 0;
upper = x.size();
while (lower + 1 != upper) {
mid = (upper + lower) / 2;
partition(x, lower, mid, upper); // O(upper - lower) time
sumb = 0;
maxb = 0; // assuming non-negative to avoid use of flags
for (i = lower; i < mid; i++) {
sumb += x[i];
maxb = max(maxb, x[i]);
}
if (sumBelow + sumb + maxb * (x.size() - mid) <= s) {
lower = mid;
sumBelow += sumb;
} else {
upper = mid;
}
}
K = (s - sumBelow) / (x.size() - lower);
if (K > maxElement(x)) return error();
else return K;
}
Upvotes: 5
Reputation: 26586
Here's my solution. I am basically doing a binary search for the value of K in the range [0, max(A)]. While this avoids having to sort the array first (thus preserving the original array), it is still O(n*log(k)) where n is the number of elements in A and k is the maximum value in A.
#! /usr/bin/env python
from itertools import imap
def findK(A,S):
lo,hi=0,max(A)
while lo<hi:
mid=(hi+lo+1)>>1
result=sum(imap(lambda x: x if x<mid else mid,A))
if result<=S:
lo=mid
else:
hi=mid-1
return lo
if __name__=='__main__':
print findK(A=[90,30,100,40,20],S = 210)
Upvotes: 1
Reputation: 1104
First sort the list smallest to largest and find how long it is. Then start adding up the numbers one by one. At each step, also find a lower limit on what the sum could be - what the sum of the whole list would be, if all of the rest of the numbers you haven't added in yet are the same as the current number.
At some point this lower limit on the sum will go from being smaller than S', to larger than S', and at that point you can do some arithmetic to determine what the cutoff should be. eg (C = current sum, L = lower limit on total sum):
start [90 30 100 40 20] sort [20 30 40 90 100] start adding up the sum C1 = 20 L1 = 20 + 4*20 = 100 < 210 C2 = 20 + 30 = 50 L2 = 50 + 3*30 = 140 < 210 C3 = 50 + 40 = 90 L3 = 90 + 2*40 = 170 < 210 C4 = 90 + 90 = 180 L4 = 180 + 1*90 = 270 > 210 //too big! S' = C3 + K*2 therefore K = (S'-C3)/2 = 60
Upvotes: 6