include "globals.mzn"; enum SUBJECTS; int: blocksAllowed; % the number of blocks allowed int: maxTaughtSubjects; % the maximum number of scheduled subjects to be taught, not used int: maxSubjectsPerBlock; % the maximum number of subjects that can be in a block int: minOccurenceSubject; % the minimum number of times a subject has to occur array[int,int] of SUBJECTS: selection; % set of k-selections of subjects set of int: SELECTIONS = index_set_1of2(selection); set of int: CHOICE = index_set_2of2(selection); set of int: BLOCKS = 1..blocksAllowed; array[BLOCKS,SUBJECTS] of var 0..1: block; % block[b,s] = 1 <-> subject s is taught in block b array[SELECTIONS,CHOICE] of var BLOCKS: selectedSubject; array[BLOCKS] of var 0..1: blockUsed; % blockUsed[i] = 1 <-> there is a subject in block i constraint forall(b in BLOCKS,s in SELECTIONS,j in CHOICE)(block[b,selection[s,j]] = 0 -> selectedSubject[s,j] != b); constraint forall(s in SELECTIONS)(alldifferent([selectedSubject[s,j] | j in CHOICE])); constraint forall(b in BLOCKS)(sum(row(block,b)) <= maxSubjectsPerBlock); constraint forall(s in SUBJECTS)(sum(col(block,s)) >= minOccurenceSubject); % symmetry break ... only useful in optimisation or unsat? constraint forall(b in 1..blocksAllowed-1)(lex_greatereq(row(block,b),row(block,b+1))); constraint forall(b in BLOCKS)(blockUsed[b] = 1 <-> sum(row(block,b)) > 0); % flattened array of blocks array[int] of var 0..1: dec = [block[i,j] | i in BLOCKS,j in SUBJECTS]; % search annotation: dec are the decision variables, fail first (so what), select 1 before 0, complete search %solve :: int_search(dec,first_fail,indomain_max,complete) satisfy; %solve :: int_search(dec,first_fail,indomain_max,complete) minimize sum(blockUsed); solve minimize sum(blockUsed); output [join("\n", ["block \(b): " ++ join("",[(if fix(block[b,s]) = 1 then format(SUBJECTS[s]) ++ " " else "" endif) | s in SUBJECTS]) | b in BLOCKS])]; % % NOTES: % - to minimize number of blocks used minimize the maximum block number in flattened array selectedSubject % - get jth column of 2d array -> col(x,j) % - get ith row of 2d array -> row(x,i) % - index set of 1d array -> index_set(x) % - row index set of 2d array -> index_set_1of2(x) % - col index set of 2d array -> index_set_2of2(x) %