The MIP model by Erwin Kalvelagen is a good way to solve the problem, and is probably quite efficient.
As an alternative, the following MiniZinc model is a fairly straight-forward model based on the problem description. Additionaly, it can be used with different types of solvers.
% The data-set of a problem
enum Teams;
enum Individuals;
enum Timeslots;
array[Teams] of set of Individuals: team_members;
% The meeting time for each team
array[Teams] of var Timeslots: meeting_time;
% The count of overlaps
var int: overlaps = sum(t1, t2 in Teams where t1 < t2) (
if meeting_time[t1] = meeting_time[t2] then
card(team_members[t1] intersect team_members[t2])
else
0
endif
);
% Solve minimizing the number of overlaps using the solvers default strategy
solve minimize overlaps;
% Variables for counting the conflicts for each individual, used only for output
array[Individuals, Timeslots] of var int: conflicts = array2d(Individuals, Timeslots, [
let {
var int: meetings = count(t in Teams where i in team_members[t]) (
meeting_time[t] = m
)
} in
meetings
| i in Individuals, m in Timeslots
]);
output [
"Times: \(meeting_time)\n",
"Total overlaps: \(overlaps)\n",
"Conflicts: \n", show2d(conflicts),"\n",
];
The data can be supplied in a dzn
-file. Here is a file corresponding to the instance in Kalvelagens reply
Timeslots = {M1, M2, M3};
Individuals = {I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16, I17, I18, I19, I20};
Teams = {T1, T2, T3, T4, T5, T6};
team_members = [
{I1, I6, I10, I11, I13, I15, I17, I19},
{I8, I10, I11, I14, I15},
{I2, I3, I5, I6, I14, I17, I20},
{I8, I14, I18, I19},
{I3, I4, I10, I11, I12, I17, I20},
{I1, I4, I7, I11, I12, I13, I17},
];
In MiniZinc version 2.7.6 with the default solver (Gecode 6.3.0, with statistics), the output is as follows
[...]
----------
Times: [M2, M1, M1, M3, M3, M1]
Total overlaps: 3
Conflicts:
[| M1: M2: M3:
| I1: 1, 1, 0
| I2: 1, 0, 0
| I3: 1, 0, 1
| I4: 1, 0, 1
| I5: 1, 0, 0
| I6: 1, 1, 0
| I7: 1, 0, 0
| I8: 1, 0, 1
| I9: 0, 0, 0
| I10: 1, 1, 1
| I11: 2, 1, 1
| I12: 1, 0, 1
| I13: 1, 1, 0
| I14: 2, 0, 1
| I15: 1, 1, 0
| I16: 0, 0, 0
| I17: 2, 1, 1
| I18: 0, 0, 1
| I19: 0, 1, 1
| I20: 1, 0, 1
|]
----------
==========
%%%mzn-stat: failures=23
%%%mzn-stat: initTime=0.000784
%%%mzn-stat: nodes=61
%%%mzn-stat: peakDepth=7
%%%mzn-stat: propagations=3096
%%%mzn-stat: propagators=122
%%%mzn-stat: restarts=0
%%%mzn-stat: solutions=8
%%%mzn-stat: solveTime=0.000591
%%%mzn-stat: variables=121
%%%mzn-stat-end
%%%mzn-stat: nSolutions=8
%%%mzn-stat-end
Finished in 68msec.
Using OR-Tools 9.7 as the solver, gives the following statistics
[...]
Times: [M1, M2, M1, M3, M3, M2]
Total overlaps: 3
Conflicts:
[| M1: M2: M3:
| I1: 1, 1, 0
| I2: 1, 0, 0
| I3: 1, 0, 1
| I4: 0, 1, 1
| I5: 1, 0, 0
| I6: 2, 0, 0
| I7: 0, 1, 0
| I8: 0, 1, 1
| I9: 0, 0, 0
| I10: 1, 1, 1
| I11: 1, 2, 1
| I12: 0, 1, 1
| I13: 1, 1, 0
| I14: 1, 1, 1
| I15: 1, 1, 0
| I16: 0, 0, 0
| I17: 2, 1, 1
| I18: 0, 0, 1
| I19: 1, 0, 1
| I20: 1, 0, 1
|]
----------
==========
%%%mzn-stat: nSolutions=9
%%%mzn-stat-end
%%%mzn-stat: boolVariables=63
%%%mzn-stat: failures=26
%%%mzn-stat: objective=3
%%%mzn-stat: objectiveBound=3
%%%mzn-stat: propagations=5882
%%%mzn-stat: solveTime=0.009147
%%%mzn-stat-end
Finished in 98msec.
Using the included HiGHS MIP solver gives the following statistics
%%%mzn-stat: nodes=7
%%%mzn-stat: objective=3
%%%mzn-stat: objectiveBound=3
%%%mzn-stat: solveTime=0.2083
%%%mzn-stat-end
Times: [M3, M2, M3, M1, M1, M2]
Total overlaps: 3
Conflicts:
[| M1: M2: M3:
| I1: 0, 1, 1
| I2: 0, 0, 1
| I3: 1, 0, 1
| I4: 1, 1, 0
| I5: 0, 0, 1
| I6: 0, 0, 2
| I7: 0, 1, 0
| I8: 1, 1, 0
| I9: 0, 0, 0
| I10: 1, 1, 1
| I11: 1, 2, 1
| I12: 1, 1, 0
| I13: 0, 1, 1
| I14: 1, 1, 1
| I15: 0, 1, 1
| I16: 0, 0, 0
| I17: 1, 1, 2
| I18: 1, 0, 0
| I19: 1, 0, 1
| I20: 1, 0, 1
|]
----------
==========
%%%mzn-stat: nodes=7
%%%mzn-stat: objective=3
%%%mzn-stat: objectiveBound=3
%%%mzn-stat: solveTime=0.2083
%%%mzn-stat-end
%%%mzn-stat: nSolutions=1
%%%mzn-stat-end
Finished in 291msec.
Since this is a fairly simple instance, there is no real insight in testing the different solvers. With larger instances, there is probably a lot more to be gained from choosing the right solver and adding parallel search.
In addition, the conflicts
array of variables in the model is just added to get some pretty output, it is not useful for solving the problem. To get the best speed, the variables (and the output statement that uses them) should probably be removed.