Files
ortools-clone/examples/contrib/crew.cs
2020-10-26 18:41:49 +01:00

244 lines
7.1 KiB
C#

//
// Copyright 2012 Hakan Kjellerstrand
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Google.OrTools.ConstraintSolver;
public class Crew {
/**
*
* Crew allocation problem in Google CP Solver.
*
* From Gecode example crew
* examples/crew.cc
* """
* Example: Airline crew allocation
*
* Assign 20 flight attendants to 10 flights. Each flight needs a certain
* number of cabin crew, and they have to speak certain languages.
* Every cabin crew member has two flights off after an attended flight.
* """
*
* Also see http://www.hakank.org/or-tools/crew.pl
*
*/
private static void Solve(int sols = 1, int minimize = 0) {
Solver solver = new Solver("Crew");
//
// Data
//
string[] names = {"Tom", "David", "Jeremy", "Ron", "Joe",
"Bill", "Fred", "Bob", "Mario", "Ed",
"Carol", "Janet", "Tracy", "Marilyn", "Carolyn",
"Cathy", "Inez", "Jean", "Heather", "Juliet"};
int num_persons = names.Length;
//
// Attributes of the crew
//
int[, ] attributes = {
// steward, hostess, french, spanish, german
{1, 0, 0, 0, 1}, // Tom = 0
{1, 0, 0, 0, 0}, // David = 1
{1, 0, 0, 0, 1}, // Jeremy = 2
{1, 0, 0, 0, 0}, // Ron = 3
{1, 0, 0, 1, 0}, // Joe = 4
{1, 0, 1, 1, 0}, // Bill = 5
{1, 0, 0, 1, 0}, // Fred = 6
{1, 0, 0, 0, 0}, // Bob = 7
{1, 0, 0, 1, 1}, // Mario = 8
{1, 0, 0, 0, 0}, // Ed = 9
{0, 1, 0, 0, 0}, // Carol = 10
{0, 1, 0, 0, 0}, // Janet = 11
{0, 1, 0, 0, 0}, // Tracy = 12
{0, 1, 0, 1, 1}, // Marilyn = 13
{0, 1, 0, 0, 0}, // Carolyn = 14
{0, 1, 0, 0, 0}, // Cathy = 15
{0, 1, 1, 1, 1}, // Inez = 16
{0, 1, 1, 0, 0}, // Jean = 17
{0, 1, 0, 1, 1}, // Heather = 18
{0, 1, 1, 0, 0} // Juliet = 19
};
//
// Required number of crew members.
//
// The columns are in the following order:
// staff : Overall number of cabin crew needed
// stewards : How many stewards are required
// hostesses : How many hostesses are required
// french : How many French speaking employees are required
// spanish : How many Spanish speaking employees are required
// german : How many German speaking employees are required
//
int[, ] required_crew = {
{4, 1, 1, 1, 1, 1}, // Flight 1
{5, 1, 1, 1, 1, 1}, // Flight 2
{5, 1, 1, 1, 1, 1}, // ..
{6, 2, 2, 1, 1, 1}, {7, 3, 3, 1, 1, 1}, {4, 1, 1, 1, 1, 1},
{5, 1, 1, 1, 1, 1}, {6, 1, 1, 1, 1, 1}, {6, 2, 2, 1, 1, 1}, // ...
{7, 3, 3, 1, 1, 1} // Flight 10
};
int num_flights = required_crew.GetLength(0);
//
// Decision variables
//
IntVar[, ] crew =
solver.MakeIntVarMatrix(num_flights, num_persons, 0, 1, "crew");
IntVar[] crew_flat = crew.Flatten();
// number of working persons
IntVar num_working = solver.MakeIntVar(1, num_persons, "num_working");
//
// Constraints
//
// number of working persons
IntVar[] nw = new IntVar[num_persons];
for (int p = 0; p < num_persons; p++) {
IntVar[] tmp = new IntVar[num_flights];
for (int f = 0; f < num_flights; f++) {
tmp[f] = crew[f, p];
}
nw[p] = tmp.Sum() > 0;
}
solver.Add(nw.Sum() == num_working);
for (int f = 0; f < num_flights; f++) {
// size of crew
IntVar[] tmp = new IntVar[num_persons];
for (int p = 0; p < num_persons; p++) {
tmp[p] = crew[f, p];
}
solver.Add(tmp.Sum() == required_crew[f, 0]);
// attributes and requirements
for (int a = 0; a < 5; a++) {
IntVar[] tmp2 = new IntVar[num_persons];
for (int p = 0; p < num_persons; p++) {
tmp2[p] = (crew[f, p] * attributes[p, a]).Var();
}
solver.Add(tmp2.Sum() >= required_crew[f, a + 1]);
}
}
// after a flight, break for at least two flights
for (int f = 0; f < num_flights - 2; f++) {
for (int i = 0; i < num_persons; i++) {
solver.Add(crew[f, i] + crew[f + 1, i] + crew[f + 2, i] <= 1);
}
}
// extra contraint: all must work at least two of the flights
/*
for(int p = 0; p < num_persons; p++) {
IntVar[] tmp = new IntVar[num_flights];
for(int f = 0; f < num_flights; f++) {
tmp[f] = crew[f,p];
}
solver.Add(tmp.Sum() >= 2);
}
*/
//
// Search
//
DecisionBuilder db = solver.MakePhase(
crew_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE);
if (minimize > 0) {
OptimizeVar obj = num_working.Minimize(1);
solver.NewSearch(db, obj);
} else {
solver.NewSearch(db);
}
int num_solutions = 0;
while (solver.NextSolution()) {
num_solutions++;
Console.WriteLine("Solution #{0}", num_solutions);
Console.WriteLine("Number working: {0}", num_working.Value());
for (int f = 0; f < num_flights; f++) {
for (int p = 0; p < num_persons; p++) {
Console.Write(crew [f, p]
.Value() +
" ");
}
Console.WriteLine();
}
Console.WriteLine("\nFlights: ");
for (int f = 0; f < num_flights; f++) {
Console.Write("Flight #{0}: ", f);
for (int p = 0; p < num_persons; p++) {
if (crew [f, p]
.Value() == 1) {
Console.Write(names[p] + " ");
}
}
Console.WriteLine();
}
Console.WriteLine("\nCrew:");
for (int p = 0; p < num_persons; p++) {
Console.Write("{0,-10}", names[p]);
for (int f = 0; f < num_flights; f++) {
if (crew [f, p]
.Value() == 1) {
Console.Write(f + " ");
}
}
Console.WriteLine();
}
Console.WriteLine();
if (num_solutions >= sols) {
break;
}
}
Console.WriteLine("\nSolutions: {0}", solver.Solutions());
Console.WriteLine("WallTime: {0}ms", solver.WallTime());
Console.WriteLine("Failures: {0}", solver.Failures());
Console.WriteLine("Branches: {0} ", solver.Branches());
solver.EndSearch();
}
public static void Main(String[] args) {
int n = 1;
int min = 0; // > 0 -> minimize num_working
if (args.Length > 0) {
n = Convert.ToInt32(args[0]);
}
if (args.Length > 1) {
min = Convert.ToInt32(args[1]);
}
Solve(n, min);
}
}