Editorial for WC '15 Contest 1 J2 - Grouping Recruits
Submitting an official solution before solving the problem yourself is a bannable offence.
This is a simple math problem. We want to divide students up into groups such that the difference between the sizes of the largest and smallest groups is minimized. An analogous situation to imagine is the process of dealing out cards one-by-one to players seated around a table – players get as equal number of cards as possible. We can choose to approach this problem using simulation or mathematics.
We can simulate the assignment of students by keeping a counter of how many students have yet to be distributed (initialized to ), as well as storing the number of students currently in each group as an array (each array cell initialized to ). Then, we would loop upwards through each group (resetting our loop variable to the index of the first group when we reach the last one), decrementing our counter for each iteration whilst incrementing the value at the current index of the array, until the number of students has reached . Finally, we would go back to the array and count the frequencies of each group size and report the answer.
This method would have worked in the contest, but it should be obvious that it is overcomplicated to write out in code. It's always a good idea to make our code as simple as possible to avoid the introduction of bugs that can take time to find and fix. This is especially true in timed contests, where we should always think a bit more about better alternate solutions before plunging straight into code. Furthermore, and happened to have small bounds of for this problem. Had the bounds been greater (say, ), our current method would likely not have executed under the time limit. We can reach a much more elegant and efficient mathematical solution by making some simple observations.
The first observation is that there are only two possibilities for the answer: either the students are evenly distributed (all group sizes are the same) or some groups will have at most more recruit than others (there are exactly two group sizes). The reason for this should be intuitive – we know that there definitely exists a way to make groups of equal sizes which are as big as possible, simply by having some students left over. The number of students left over must be smaller than , because otherwise we would create even bigger evenly-sized groups by taking students from the leftovers to distribute one-each amongst the groups (contradicting how we assumed the groups were as big as possible). Intuition is an important skill to have during programming contests because we do not have to prove anything rigorously in order to implement it. As long as we're intuitively convinced that it is right, we can go for the answer.
With that in mind, we have to actually come up with formulas. Our formulas will require the modulo operation () which takes the remainder after division. is equal to , for example. It is available in nearly all programming languages (the operator is the %
sign in both C++ and Python). So, let's consider the two cases separately:
- To check if we can evenly divide students amongst groups, we just check if the remainder of divided by is . If is , we know that there are groups of size each.
Otherwise, it must be that there are group sizes. Intuitively, we know that some groups will have size , rounded down (call these type 1 groups). Since we know that we'll have to distribute the leftover students, some groups will have a size that's exactly more than the value of rounded down (call these type 2 groups).
How many type 2 groups are there? Well that's exactly the number of leftover students there are (, since we're distributing 1 leftover student to each type 2 group)! However many type 2 groups there are, the number of type 1 groups must be exactly minus that number since there are groups total.
Note that the //
operator in Python performs integer division (division rounded down), as does the /
operator in C++, if the operands are integer types.
Comments