Cabin crew per flight | |||
flight# | #of cabin staff | flight# | #of cabin staff |
1 | 4 | 6 | 4 |
2 | 4 | 7 | 4 |
3 | 5 | 8 | 5 |
4 | 5 | 9 | 5 |
5 | 6 | 10 | 6 |
Cabin crew speaking foreign language per flight | |||
flight# | French | Spanish | German |
1 | 1 | 1 | 1 |
2 | 1 | 1 | 1 |
3 | 1 | 1 | 1 |
4 | 2 | 2 | 1 |
5 | 2 | 2 | 1 |
6 | 1 | 1 | 1 |
7 | 1 | 1 | 1 |
8 | 1 | 1 | 1 |
9 | 1 | 1 | 1 |
10 | 1 | 1 | 1 |
Male resp. female cabin crew per flight | |||||
flight# | male | female | flight# | male | female |
1 | 1 | 1 | 6 | 1 | 1 |
2 | 1 | 1 | 7 | 1 | 1 |
3 | 1 | 1 | 8 | 1 | 1 |
4 | 2 | 2 | 9 | 1 | 1 |
5 | 3 | 2 | 10 | 1 | 1 |
val maxValue = (valOf (Int.maxInt))div 2 type crewrec = {french : int list, german : int list, spanish : int list,stewardesses : int list, stewards : int list} type flightrec = {crew : int, french : int, german : int, no : int,spanish : int,stewardesses : int, stewards : int} val crew = {stewards = [1,2,3,4,5,6,7,8,9,10], stewardesses = [11,12,13,14,15,16,17,18,19,20], french = [6,17,18,20], german = [1,3,9,16,20], spanish = [5,6,7,9,14,17,19]} val flights = [{no = 1,crew = 4,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 2,crew = 5,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 3,crew = 5,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 4,crew = 6,stewards = 2,stewardesses = 2, french = 1, spanish = 1,german = 1}, {no = 5,crew = 7,stewards = 3,stewardesses = 3, french = 1, spanish = 1,german = 1}, {no = 6,crew = 4,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 7,crew = 5,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 8,crew = 6,stewards = 1,stewardesses = 1, french = 1, spanish = 1,german = 1}, {no = 9,crew = 6,stewards = 2,stewardesses = 2, french = 1, spanish = 1,german = 1}, {no = 10,crew = 7,stewards = 3,stewardesses = 3, french = 1, spanish = 1,german = 1}] fun assignCrew flights (crew:crewrec) space = let val stewards = FS.Value.make(space, FD.domainFromList(#stewards(crew))) val stewardesses = FS.Value.make(space, FD.domainFromList(#stewardesses(crew))) val french = FS.Value.make(space, FD.domainFromList(#french(crew))) val german = FS.Value.make(space, FD.domainFromList(#german(crew))) val spanish = FS.Value.make(space, FD.domainFromList(#spanish(crew))) fun teamConstraint(team,flight:flightrec)= let val n = #crew(flight) val nstew = #stewards(flight) val nhost = #stewardesses(flight) val ngerm = #german(flight) val nspan = #spanish(flight) val nfren = #french(flight) fun cardOp(nb,setv)= let val tmp1 = FS.setvar space val tmp2 = FD.range(space,(1,maxValue)) in FS.relOp(space,team,FS.INTER,setv, FS.SEQ,tmp1); FS.cardinality(space,tmp1,tmp2); post(space,FD(tmp2) `>= `nb,FD.DOM) end in FS.cardRange(space,n,n,team); Vector.app(fn(x,y)=>cardOp(x,y)) (#[(nstew,stewards),(nhost,stewardesses), (ngerm,german),(nspan,spanish),(nfren,french)]) end fun sequenceDisjoint(x::y::nil)= FS.rel(space,x,FS.DISJ,y) | sequenceDisjoint(x::y::z::xs)= (FS.rel(space,x,FS.DISJ,y); FS.rel(space,x,FS.DISJ,z); sequenceDisjoint(y::z::xs)) val crewlist = List.tabulate(List.length flights,fn x => FS.upperBound(space,#[(1,20)])) in List.app(fn(x,y)=> teamConstraint(y,x)) (ListPair.zip(flights,crewlist)); sequenceDisjoint(crewlist); FS.setvarbranch(space,Vector.fromList crewlist, FS.FSB_NONE,FS.FSB_MIN); crewlist end
The procedure teamConstraint imposes the abovementioned constraints on the individual flight cabin crew sets intersecting them with appropriate sets by FS.relOp with the set operation FS.INTER, and constrains the intersection's cardinality according to Table 11.5.1(using FS.cardinality and post).
The procedure sequenceDisjoint is responsible to ensure that every crew member may enjoy a two-flight break between two flights. It is a recursive procedure imposing FS.rel with the set operation FS.DISJ upon every 3 subsequent sets.
The actual solver declares the local variable crewlist that contains the list of sets representing the individual crew assignments. Then, the constraints of the procedure teamConstraint are imposed on crewlist by the List.app procedure, applying the data provided by flights to crewlist. The branching is straightforward and has no particularities.
The following sample data can be used to test the solver:
fun name 1 = "tom" | name 2 = "david" | name 3 = "jeremy" | name 4 = "ron" | name 5 = "joe" | name 6 = "bill" | name 7 = "fred" | name 8 = "bob" | name 9 = "mario" | name 10 = "ed" | name 11 = "carol" | name 12 = "janet" | name 13 = "tracy" | name 14 = "marilyn" | name 15 = "carolyn" | name 16 = "cathy" | name 17 = "inez" | name 18 = "jean" | name 19 = "heather" | name 20 = "juliet"; let val (s,r) = valOf(Search.searchOne(assignCrew flights crew)) in List.map(fn x => List.map(fn y => name y)x) (List.map(fn y=> domainToList y)(List.map(fn x => FS.Reflect.upperBound(s,x))(r))) end
Andreas Rossberg 2006-08-28