You can model this exactly using Integer programming as an Assignment Problem.
Variables
Let there be s students and p professors.
The decision variable
X_sp is 1 if student s takes an exam with professor p
0 otherwise
Let Limit_p be the number of exams that professor p can handle.
Handling Student Preferences
We can handle each student's preference through the costs (objective)
Let C_sp be the 'cost' of assigning student s to take an exam with professor p.
We keep progressively making the costs higher as the preference decreases.
C_sp = 1 if p is the *first* preference of s
C_sp = 2 if p is the *second* preference of s
C_sp = 3 if p is the *third* preference of s
...
C_sp = n if p is the *n th* preference of s
Formulation
Case 1: One Exam
Min (sum over s)(sum over p) C_sp X_sp
subject to:
(sum over p) X_sp = 1 for each student s # Every student must take an exam
(sum over s) X_sp <= Limit_p for each professor p # Every prof can only handle so many exams
X_sp = {0,1} # binary
Case 2: student takes two Exams
Min (sum over s)(sum over p) C_sp X_sp
subject to:
(sum over p) X_sp = 2 for each student s # Every student must take an exam
(sum over s) X_sp <= Limit_p for each professor p # Every prof can only handle so many exams
X_sp = {0,1} # binary
Taking care of no student can take both exams with the same professor
Normally, we'd have to introduce indicator variables Y_sp
to denote whether or not a student has taken an exam with professor p. However, in this case we don't even have to do that. The fact that X_sp
is binary will ensure that no student ends up taking 2 exams with the same professor.
If the student has a preference of which exam they'd like to take with a professor, then adding a subscript e
to the decision variable is required. X_spe
You can solve this either through the Hungarian method, or easier still, any available implementation of LP/IP solver.
The Hungarian algorithm's complexity is O(n^3), and since we haven't introduced any side constraints to spoil the integrality property, the linear solution will always be integral.
Update (A little bit of theory)
Why are the solutions integral? To understand why the solutions are guaranteed to be integral, a little bit of theory is necessary.
Typically, when confronted with an IP, the first thought is to try solving the linear relaxation (Forget the integral values requirements and try.) This will give a lower bound to minimization problems. There are usually a few so-called complicating constraints that make integer solutions very difficult to find. One solution technique is to relax those by throwing them to the objective function, and solving the simpler problem (the Lagrangian Relaxation problem.)
Now, if the solution to the Lagrangian doesn't change whether or not you have integrality (as is the case with Assignment Problems) then you always get integer solutions.
For those who are really interested in learning about Integrality and Lagrangian duals, this reference is quite readable. The example in Section 3 specifically covers the Assignment Problem.
Free MILP Solvers: This SO question is quite exhaustive for that.
Hope that helps.