The Mixed Integer Linear Programming Solver

Example 14.1 Scheduling

The following example has been adapted from the example "A Scheduling Problem" in Chapter 4, "The LP Procedure" (SAS/OR User's Guide: Mathematical Programming Legacy Procedures).

Scheduling is a common application area in which mixed integer linear programming techniques are used. In this example, you have eight one-hour time slots in each of five days. You have to assign four employees to these time slots so that each slot is covered every day. You allow the employees to specify preference data for each slot on each day. In addition, the following constraints must be satisfied:

  • Each employee has some time slots for which he or she is unavailable (OneEmpPerSlot).

  • Each employee must have either time slot 4 or time slot 5 off for lunch (EmpMustHaveLunch).

  • Each employee can work at most two time slots in a row (AtMost2ConSlots).

  • Each employee can work only a specified number of hours in the week (WeeklyHoursLimit).

To formulate this problem, let i denote a person, j denote a time slot, and k denote a day. Then, let x Subscript i j k Baseline equals 1 if person i is assigned to time slot j on day k, and 0 otherwise. Let p Subscript i j k denote the preference of person i for slot j on day k. Let h Subscript i denote the number of hours in a week that person i will work. The formulation of this problem follows:

StartLayout 1st Row 1st Column max 2nd Column sigma-summation Underscript i j k Endscripts p Subscript i j k Baseline x Subscript i j k 3rd Column Blank 2nd Row 1st Column normal s period normal t period 2nd Column sigma-summation Underscript i Endscripts x Subscript i j k 3rd Column equals 4th Column 1 5th Column for-all j comma k 6th Column left-parenthesis normal upper O normal n normal e normal upper E normal m normal p normal upper P normal e normal r normal upper S normal l normal o normal t right-parenthesis 3rd Row 1st Column Blank 2nd Column x Subscript i Baseline 4 k Baseline plus x Subscript i Baseline 5 k 3rd Column less-than-or-equal-to 4th Column 1 5th Column for-all i comma k 6th Column left-parenthesis normal upper E normal m normal p normal upper M normal u normal s normal t normal upper H normal a normal v normal e normal upper L normal u normal n normal c normal h right-parenthesis 4th Row 1st Column Blank 2nd Column x Subscript i comma script l comma k Baseline plus x Subscript i comma script l plus 1 comma k Baseline plus x Subscript i comma script l plus 2 comma k 3rd Column less-than-or-equal-to 4th Column 2 5th Column for-all i comma k comma normal a normal n normal d l less-than-or-equal-to 6 6th Column left-parenthesis normal upper A normal t normal upper M normal o normal s normal t Baseline 2 normal upper C normal o normal n normal upper S normal l normal o normal t normal s right-parenthesis 5th Row 1st Column Blank 2nd Column sigma-summation Underscript j k Endscripts x Subscript i j k 3rd Column less-than-or-equal-to 4th Column h Subscript i 5th Column for-all i 6th Column left-parenthesis normal upper W normal e normal e normal k normal l normal y normal upper H normal o normal u normal r normal s normal upper L normal i normal m normal i normal t right-parenthesis 6th Row 1st Column Blank 2nd Column x Subscript i j k 3rd Column equals 4th Column 0 5th Column for-all i comma j comma k normal s period normal t period p Subscript i j k Baseline greater-than 0 7th Row 1st Column Blank 2nd Column x Subscript i j k Baseline element-of StartSet 0 comma 1 EndSet 3rd Column Blank 4th Column Blank 5th Column for-all i comma j comma k EndLayout

The following data set preferences gives the preferences for each individual, time slot, and day. A 10 represents the most desirable time slot, and a 1 represents the least desirable time slot. In addition, a 0 indicates that the time slot is not available. The data set maxhours gives the maximum number of hours each employee can work per week.


data preferences;
   input name $ slot mon tue wed thu fri;
   datalines;
marc  1    10 10 10 10 10
marc  2     9  9  9  9  9
marc  3     8  8  8  8  8
marc  4     1  1  1  1  1
marc  5     1  1  1  1  1
marc  6     1  1  1  1  1
marc  7     1  1  1  1  1
marc  8     1  1  1  1  1
mike  1    10  9  8  7  6
mike  2    10  9  8  7  6
mike  3    10  9  8  7  6
mike  4    10  3  3  3  3
mike  5     1  1  1  1  1
mike  6     1  2  3  4  5
mike  7     1  2  3  4  5
mike  8     1  2  3  4  5
bill  1    10 10 10 10 10
bill  2     9  9  9  9  9
bill  3     8  8  8  8  8
bill  4     0  0  0  0  0
bill  5     1  1  1  1  1
bill  6     1  1  1  1  1
bill  7     1  1  1  1  1
bill  8     1  1  1  1  1
bob   1    10  9  8  7  6
bob   2    10  9  8  7  6
bob   3    10  9  8  7  6
bob   4    10  3  3  3  3
bob   5     1  1  1  1  1
bob   6     1  2  3  4  5
bob   7     1  2  3  4  5
bob   8     1  2  3  4  5
;

data maxhours;
   input name $ hour;
   datalines;
marc  20
mike  20
bill  20
bob   20
;

Using PROC OPTMODEL, you can model and solve the scheduling problem as follows:

proc optmodel;

   /* read in the preferences and max hours from the data sets */
   set <string,num> DailyEmployeeSlots;
   set <string>     Employees;

   set <num>    TimeSlots = (setof {<name,slot> in DailyEmployeeSlots} slot);
   set <string> WeekDays  = {"mon","tue","wed","thu","fri"};

   num WeeklyMaxHours{Employees};
   num PreferenceWeights{DailyEmployeeSlots,Weekdays};
   num NSlots = card(TimeSlots);

   read data preferences into DailyEmployeeSlots=[name slot]
        {day in Weekdays} <PreferenceWeights[name,slot,day] = col(day)>;
   read data maxhours into Employees=[name] WeeklyMaxHours=hour;

   /* declare the binary assignment variable x[i,j,k] */
   var Assign{<name,slot> in DailyEmployeeSlots, day in Weekdays} binary;

   /* for each p[i,j,k] = 0, fix x[i,j,k] = 0 */
   for {<name,slot> in DailyEmployeeSlots, day in Weekdays:
       PreferenceWeights[name,slot,day] = 0}
         fix Assign[name,slot,day] = 0;

   /* declare the objective function */
   max TotalPreferenceWeight =
      sum{<name,slot> in DailyEmployeeSlots, day in Weekdays}
         PreferenceWeights[name,slot,day] * Assign[name,slot,day];

   /* declare the constraints */
   con OneEmpPerSlot{slot in TimeSlots, day in Weekdays}:
      sum{name in Employees} Assign[name,slot,day] = 1;

   con EmpMustHaveLunch{name in Employees, day in Weekdays}:
      Assign[name,4,day] + Assign[name,5,day] <= 1;

   con AtMost2ConsSlots{name in Employees, start in 1..NSlots-2,
                            day in Weekdays}:
      Assign[name,start,day] + Assign[name,start+1,day]
            + Assign[name,start+2,day] <= 2 ;

   con WeeklyHoursLimit{name in Employees}:
      sum{slot in TimeSlots, day in Weekdays} Assign[name,slot,day]
           <= WeeklyMaxHours[name];

   /* solve the model */
   solve with milp;

   /* clean up the solution */
   for {<name,slot> in DailyEmployeeSlots, day in Weekdays}
      Assign[name,slot,day] = round(Assign[name,slot,day],1e-6);

   str assigned_employee {TimeSlots, Weekdays} init '';
   for {slot in TimeSlots, day in Weekdays} do;
      for {name in Employees: Assign[name,slot,day] > 0} do;
         assigned_employee[slot,day] = name;
         leave;
      end;
   end;

   create data report from [slot]=TimeSlots
      {day in Weekdays} <col(day)=assigned_employee[slot,day]>;
quit;

The following statements demonstrate how to use the PRINT procedure to display a schedule that shows how the eight time slots are covered for the week:

/* report the solution */
title 'Reported Solution';
proc print data=report;
   id slot;
run;

The output from the preceding code is displayed in Output 14.1.1.

Output 14.1.1: Scheduling Reported Solution

Reported Solution

slotmontuewedthufri
1marcmarcmarcmarcmarc
2mikemarcmarcmarcmarc
3mikemikemikebillbill
4bobmikemikemikemike
5marcmarcmarcmarcmarc
6marcmikemikemikemike
7mikemikemikemikemike
8marcbobbobbobbob


Last updated: March 04, 2026