This is a somewhat classic programming-contest problem.
The answer is simple: do a basic binary search on volume V
(the final answer).
(Note the title says M people, yet the problem description says K. I'll be using M
)
Given a volume V
during the search, you iterate through all of the cakes, calculating how many people each cake can "feed" with single-flavor slices (fed += floor(Vi/V)
). If you reach M
(or 'K') people "fed" before you're out of cakes, this means you can obviously also feed M
people with any volume < V
with whole single-flavor slices, by simply consuming the same amount of (smaller) slices from each cake. If you run out of cakes before reaching M
slices, it means you cannot feed the people with any volume > V
either, as that would consume even more cake than what you've already failed with. This satisfies the condition for a binary search, which will lead you to the highest volume V of single-flavor slices that can be given to M people.
The complexity is O(n * log((sum(Vi)/m)/eps) )
. Breakdown: the binary search takes log((sum(Vi)/m)/eps) iterations, considering the upper bound of sum(Vi)/m
cake for each person (when all the cakes get consumed perfectly). At each iteration, you have to pass through at most all N
cakes. eps
is the precision of your search and should be set low enough, no higher than the minimum non-zero difference between the volume of two cakes, divided by M*2
, so as to guarantee a correct answer. Usually you can just set it to an absolute precision such as 1e-6
or 1e-9
.
To speed things up for the average case, you should sort the cakes in decreasing order, such that when you are trying a large volume, you instantly discard all the trailing cakes with total volume < V
(e.g. you have one cake of volume 10^6
followed by a bunch of cakes of volume 1.0
. If you're testing a slice volume of 2.0
, as soon as you reach the first cake of volume 1.0
you can already return that this run failed to provide M
slices)
Edit:
The search is actually done with floating point numbers, e.g.:
double mid, lo = 0, hi = sum(Vi)/people;
while(hi - lo > eps){
mid = (lo+hi)/2;
if(works(mid)) lo = mid;
else hi = mid;
}
final_V = lo;
By the end, if you really need more precision than your chosen eps
, you can simply take an extra O(n) step:
// (this step is exclusively to retrieve an exact answer from the final
// answer above, if a precision of 'eps' is not acceptable)
foreach (cake_volume vi){
int slices = round(vi/final_V);
double difference = abs(vi-(final_V*slices));
if(difference < best){
best = difference;
volume = vi;
denominator = slices;
}
}
// exact answer is volume/denominator