コード例 #1
0
        static int DetermineTermFromSolution(
            ConstraintSolverSolution solution,
            CspTerm[][] terms,
            int currentHouseNumber,
            IEnumerable <int> houseNubmers)
        {
            if (solution == null)
            {
                throw new ArgumentNullException("solution");
            }
            if (terms == null)
            {
                throw new ArgumentNullException("terms");
            }

            foreach (var houseNumber in houseNubmers)
            {
                object term;
                solution.TryGetValue(terms[currentHouseNumber][houseNumber],
                                     out term);

                if ((int)term == 1)
                {
                    return(houseNumber);
                }
            }
            return(0);
        }
コード例 #2
0
        public void Solve(int?[,] field)
        {
            ConstraintSystem S = ConstraintSystem.CreateSolver();
            CspDomain        Z = S.CreateIntegerInterval(1, 9);

            CspTerm[][] sudoku = S.CreateVariableArray(Z, "cell", 9, 9);
            for (int row = 0; row < 9; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    if (field[row, col] > 0)
                    {
                        S.AddConstraints(S.Equal(field[row, col] ?? 0, sudoku[row][col]));
                    }
                }
                S.AddConstraints(S.Unequal(GetSlice(sudoku, row, row, 0, 8)));
            }
            for (int col = 0; col < 9; col++)
            {
                S.AddConstraints(S.Unequal(GetSlice(sudoku, 0, 8, col, col)));
            }
            for (int a = 0; a < 3; a++)
            {
                for (int b = 0; b < 3; b++)
                {
                    S.AddConstraints(S.Unequal(GetSlice(sudoku, a * 3, a * 3 + 2, b * 3, b * 3 + 2)));
                }
            }
            ConstraintSolverSolution soln = S.Solve();

            if (!soln.HasFoundSolution)
            {
                throw new NoSolutionException("Для поставленной цифры нет решение!");
            }


            object[] h = new object[9];
            for (int row = 0; row < 9; row++)
            {
                if ((row % 3) == 0)
                {
                    System.Console.WriteLine();
                }
                for (int col = 0; col < 9; col++)
                {
                    soln.TryGetValue(sudoku[row][col], out h[col]);
                }
                System.Console.WriteLine("{0}{1}{2} {3}{4}{5} {6}{7}{8}", h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8]);
            }
        }
コード例 #3
0
        /// <summary>
        /// Knapsack enumerator -- enumerate up to "numAnswers" combinations of "weights" such that the sum of the weights is less than the weight limit.
        /// It places the patterns of items inside the list of patterns.  The efficiency parameter ensures that we don't output any which use less than "efficiency" percent
        /// off the weightlimit.
        /// </summary>
        /// <param name="maxAnswers">maximum number of combinations to get out.  Limits runtime.  If zero return all.</param>
        /// <param name="weights">weight of each item to go into the knapsack</param>
        /// <param name="weightLimit">knapsack weight limit</param>
        /// <param name="efficiency">limit patterns to use at least this % of the weight limit (between 0.0 and 1.0) </param>
        /// <param name="patterns">output list of patterns of inclusion of the weights.</param>
        public static void SolveKnapsack(int maxAnswers, int[] weights, int weightLimit, double efficiency, out List <int[]> patterns)
        {
            // convenience value.
            int NumItems            = weights.Length;
            ConstraintSystem solver = ConstraintSystem.CreateSolver();
            CspDomain        dom    = solver.CreateIntegerInterval(0, weightLimit);

            CspTerm knapsackSize = solver.Constant(weightLimit);

            // these represent the quantity of each item.
            CspTerm[] itemQty     = solver.CreateVariableVector(dom, "Quantity", NumItems);
            CspTerm[] itemWeights = new CspTerm[NumItems];

            for (int cnt = 0; cnt < NumItems; cnt++)
            {
                itemWeights[cnt] = solver.Constant(weights[cnt]);
            }

            // contributors to the weight (weight * variable value)
            CspTerm[] contributors = new CspTerm[NumItems];
            for (int cnt = 0; cnt < NumItems; cnt++)
            {
                contributors[cnt] = itemWeights[cnt] * itemQty[cnt];
            }

            // single constraint
            CspTerm knapSackCapacity = solver.GreaterEqual(knapsackSize, solver.Sum(contributors));

            solver.AddConstraints(knapSackCapacity);

            // must be efficient
            CspTerm knapSackAtLeast = solver.LessEqual(knapsackSize * efficiency, solver.Sum(contributors));

            solver.AddConstraints(knapSackAtLeast);

            // start counter and allocate a list for the results.
            int nanswers = 0;

            patterns = new List <int[]>();

            ConstraintSolverSolution sol = solver.Solve();

            while (sol.HasFoundSolution)
            {
                int[] pattern = new int[NumItems];
                // extract this pattern from the enumeration.
                for (int cnt = 0; cnt < NumItems; cnt++)
                {
                    object val;
                    sol.TryGetValue(itemQty[cnt], out val);
                    pattern[cnt] = (int)val;
                }
                // add it to the output.
                patterns.Add(pattern);
                nanswers++;
                // stop if we reach the limit of results.
                if (maxAnswers > 0 && nanswers >= maxAnswers)
                {
                    break;
                }
                sol.GetNext();
            }
        }
コード例 #4
0
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Seleccione el método por el que desea resolver el problema:\n1 Programación por restricciones\n2 Programación Lineal");
                switch (int.Parse(Console.ReadLine()))
                {
                case 1:
                    SolverContext context = new SolverContext();
                    Model         model   = context.CreateModel();
                    // Creación de variables
                    Decision x1 = new Decision(Domain.Integer, "x1");
                    model.AddDecision(x1);
                    Decision x2 = new Decision(Domain.Integer, "x2");
                    model.AddDecision(x2);
                    Decision x3 = new Decision(Domain.Integer, "x3");
                    model.AddDecision(x3);
                    // Creación de limites
                    model.AddConstraint("limitX1", 50 <= x1 <= 300);
                    model.AddConstraint("limitX2", 100 <= x2 <= 200);
                    model.AddConstraint("limitX3", 20 <= x3 <= 1000);
                    // Creación de restricciones
                    model.AddConstraint("restriccion1", 200 <= (x1 + x2 + x3) <= 280);
                    model.AddConstraint("restriccion2", 100 <= (x1 + (3 * x3)) <= 2000);
                    model.AddConstraint("restriccion3", 50 <= ((2 + x1) + (4 * x3)) <= 1000);
                    // Función objetivo
                    model.AddGoal("maximo", GoalKind.Maximize, -(4 * x1) - (2 * x2) + x3);
                    // Solucion
                    Solution solucion = context.Solve(new SimplexDirective());
                    Report   reporte  = solucion.GetReport();
                    // Imprimir
                    Console.WriteLine(reporte);
                    Console.ReadLine();
                    break;

                case 2:
                    //Solucionador específico
                    ConstraintSystem csp = ConstraintSystem.CreateSolver();
                    // Creacíón de variables
                    CspTerm sx1 = csp.CreateVariable(csp.CreateIntegerInterval(50, 300), "x1");
                    CspTerm sx2 = csp.CreateVariable(csp.CreateIntegerInterval(100, 200), "x2");
                    CspTerm sx3 = csp.CreateVariable(csp.CreateIntegerInterval(20, 1000), "x3");
                    // Creación de restricciones
                    csp.AddConstraints(200 <= (sx1 + sx2 + sx3) <= 280,
                                       100 <= sx1 + (3 * sx2) <= 2000,
                                       50 <= (2 * sx1) + (4 * sx3) <= 1000);

                    // Solución
                    ConstraintSolverSolution cspSolucion = csp.Solve();
                    int numero = 1;
                    while (cspSolucion.HasFoundSolution)
                    {
                        object rx1, rx2, rx3;
                        if (!cspSolucion.TryGetValue(sx1, out rx1) || !cspSolucion.TryGetValue(sx2, out rx2) || !cspSolucion.TryGetValue(sx3, out rx3))
                        {
                            throw new InvalidProgramException("No se encontro solución");
                        }
                        Console.WriteLine(String.Format("Solución {0} :\nx1={1}\nx2={2}\nx3={3}", numero, rx1, rx2, rx3));
                        numero += 1;
                        cspSolucion.GetNext();
                    }

                    /*
                     * //Solucionador específico
                     * SimplexSolver sSolver = new SimplexSolver();
                     * //Creación de variables
                     * int sx1, sx2, sx3;
                     * sSolver.AddVariable("x1", out sx1);
                     * sSolver.SetBounds(sx1, 50, 300);
                     * sSolver.AddVariable("x2", out sx2);
                     * sSolver.SetBounds(sx2, 100, 200);
                     * sSolver.AddVariable("x3", out sx3);
                     * sSolver.SetBounds(sx3,20,1000);
                     * //Creación de restricciones
                     * int r1, r2, r3, goal;
                     * sSolver.AddRow("restriccion1", out r1);
                     * sSolver.SetCoefficient(r1, sx1, 1);
                     * sSolver.SetCoefficient(r1, sx2, 1);
                     * sSolver.SetCoefficient(r1, sx3, 1);
                     * sSolver.SetBounds(r1, 200, 280);
                     * sSolver.AddRow("restriccion2", out r2);
                     * sSolver.SetCoefficient(r2, sx1, 1);
                     * sSolver.SetCoefficient(r2, sx2, 3);
                     * sSolver.SetBounds(r2, 100, 2000);
                     * sSolver.AddRow("restriccion3", out r3);
                     * sSolver.SetCoefficient(r3, sx1, 2);
                     * sSolver.SetCoefficient(r3, sx3, 4);
                     * sSolver.SetBounds(r3, 50, 1000);
                     * //Función objetivo
                     * sSolver.AddRow("objetivo", out goal);
                     * sSolver.SetCoefficient(goal, sx1, -4);
                     * sSolver.SetCoefficient(goal, sx2, -2);
                     * sSolver.SetCoefficient(goal, sx3, 1);
                     * sSolver.SetBounds(goal, Rational.NegativeInfinity,Rational.PositiveInfinity);
                     * sSolver.AddGoal(goal, 1, false);
                     * //Solución
                     * sSolver.Solve(new SimplexSolverParams());
                     * sSolver.GetReport();
                     */
                    break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
                Console.ReadLine();
            }
        }
コード例 #5
0
        public static void Run(int teamsNo)
        {
            // schedule N teams to play N-1 matches (one against every other team) with a difference
            //   of no more than 1 extra game away or home.  Note that N must be even (since every team
            //   must be paired every week).

            ConstraintSystem S = ConstraintSystem.CreateSolver();

            // The teams are numbered 0 to N-1 for simplicity in index lookups,
            //    since our arrays are zero-based.
            CspDomain Teams = S.CreateIntegerInterval(0, teamsNo - 1);


            CspTerm[][] matches = S.CreateVariableArray(Teams, "opponents", teamsNo, teamsNo - 1);

            CspTerm[][] atHome = S.CreateBooleanArray("atHome", teamsNo, teamsNo - 1);

            // each row represents the N-1 games the teams play.  The 0th week has an even-odd
            //  assignment since by symmetry that is equivalent to any other assignment and
            //  we thereby eliminate redundant solutions being enumerated.

            for (int t = 0; t < teamsNo; t++)
            {
                CspTerm atHomeSum = S.Sum(atHome[t]);
                S.AddConstraints(
                    S.Unequal(t, matches[t]),                                         // don't play self, and play every other team
                    S.LessEqual(teamsNo / 2 - 1, atHomeSum, S.Constant(teamsNo / 2)), // a balance of atHomes
                    S.Equal(t ^ 1, matches[t][0])                                     // even-odd pairing in the initial round
                    );
            }

            for (int w = 0; w < teamsNo - 1; w++)
            {
                S.AddConstraints(
                    S.Unequal(GetColumn(matches, w))              // every team appears once each week
                    );
                for (int t = 0; t < teamsNo; t++)
                {
                    S.AddConstraints(
                        S.Equal(t, S.Index(matches, matches[t][w], w)),           // Each team's pair's pair must be itself.
                        S.Equal(atHome[t][w], !S.Index(atHome, matches[t][w], w)) // Each pair is Home-Away or Away-Home.
                        );
                }
            }

            // That's it!  The problem is delivered to the solver.
            // Now to get an answer...

            //bool unsolved = true;
            ConstraintSolverSolution soln = S.Solve();

            if (soln.HasFoundSolution)
            {
                //unsolved = false;

                Console.Write("       | ");
                for (int w = 0; w < teamsNo - 1; w++)
                {
                    Console.Write("{1}Wk{0,2}", w + 1, w == 0 ? "" : " | ");
                }
                Console.WriteLine();
                Console.Write("       | ");
                for (int w = 0; w < teamsNo - 1; w++)
                {
                    Console.Write("{1}OP H", w + 1, w == 0 ? "" : " | ");
                }
                Console.WriteLine();
                Console.WriteLine("       {0}", "|" + new String('-', teamsNo * 6));
                for (int t = 0; t < teamsNo; t++)
                {
                    StringBuilder line = new StringBuilder();
                    line.AppendFormat("Team {0,2}| ", t + 1);
                    for (int w = 0; w < teamsNo - 1; w++)
                    {
                        object opponent, home;
                        if (!soln.TryGetValue(matches[t][w], out opponent))
                        {
                            throw new InvalidProgramException(matches[t][w].Key.ToString());
                        }
                        if (!soln.TryGetValue(atHome[t][w], out home))
                        {
                            throw new InvalidProgramException(atHome[t][w].Key.ToString());
                        }
                        line.AppendFormat("{2}{0,2} {1}",
                                          ((int)opponent) + 1,
                                          (int)home == 1 ? "*" : " ",
                                          w == 0 ? "" : " | "
                                          );
                        //line.Append(opponent.ToString());
                        //line.Append(((int)home == 1) ? " H," : " A,");
                    }
                    System.Console.WriteLine(line.ToString());
                }
                System.Console.WriteLine();
            }
            else
            {
                System.Console.WriteLine("No solution found.");
            }
        }
コード例 #6
0
        public static void Run()
        {
            ConstraintSystem S = ConstraintSystem.CreateSolver();

            List <KeyValuePair <CspTerm, string> > termList = new List <KeyValuePair <CspTerm, string> >();

            // create a Term between [1..5], associate it with a name for later ease of display

            NamedTerm namedTerm = delegate(string name) {
                CspTerm x = S.CreateVariable(S.CreateIntegerInterval(1, 5), name);
                termList.Add(new KeyValuePair <CspTerm, string>(x, name));
                return(x);
            };

            // the people and attributes will all be matched via the house they reside in.

            CspTerm English = namedTerm("English"), Spanish = namedTerm("Spanish"), Japanese = namedTerm("Japanese"), Italian = namedTerm("Italian"), Norwegian = namedTerm("Norwegian");
            CspTerm red = namedTerm("red"), green = namedTerm("green"), white = namedTerm("white"), blue = namedTerm("blue"), yellow = namedTerm("yellow");
            CspTerm dog = namedTerm("dog"), snails = namedTerm("snails"), fox = namedTerm("fox"), horse = namedTerm("horse"), zebra = namedTerm("zebra");
            CspTerm painter = namedTerm("painter"), sculptor = namedTerm("sculptor"), diplomat = namedTerm("diplomat"), violinist = namedTerm("violinist"), doctor = namedTerm("doctor");
            CspTerm tea = namedTerm("tea"), coffee = namedTerm("coffee"), milk = namedTerm("milk"), juice = namedTerm("juice"), water = namedTerm("water");

            S.AddConstraints(
                S.Unequal(English, Spanish, Japanese, Italian, Norwegian),
                S.Unequal(red, green, white, blue, yellow),
                S.Unequal(dog, snails, fox, horse, zebra),
                S.Unequal(painter, sculptor, diplomat, violinist, doctor),
                S.Unequal(tea, coffee, milk, juice, water),
                S.Equal(English, red),
                S.Equal(Spanish, dog),
                S.Equal(Japanese, painter),
                S.Equal(Italian, tea),
                S.Equal(1, Norwegian),
                S.Equal(green, coffee),
                S.Equal(1, green - white),
                S.Equal(sculptor, snails),
                S.Equal(diplomat, yellow),
                S.Equal(3, milk),
                S.Equal(1, S.Abs(Norwegian - blue)),
                S.Equal(violinist, juice),
                S.Equal(1, S.Abs(fox - doctor)),
                S.Equal(1, S.Abs(horse - diplomat))
                );

            bool unsolved = true;
            ConstraintSolverSolution soln = S.Solve();

            while (soln.HasFoundSolution)
            {
                unsolved = false;
                System.Console.WriteLine("solved.");
                StringBuilder[] houses = new StringBuilder[5];
                for (int i = 0; i < 5; i++)
                {
                    houses[i] = new StringBuilder(i.ToString());
                }
                foreach (KeyValuePair <CspTerm, string> kvp in termList)
                {
                    string item = kvp.Value;
                    object house;
                    if (!soln.TryGetValue(kvp.Key, out house))
                    {
                        throw new InvalidProgramException("can't find a Term in the solution: " + item);
                    }
                    houses[(int)house - 1].Append(", ");
                    houses[(int)house - 1].Append(item);
                }
                foreach (StringBuilder house in houses)
                {
                    System.Console.WriteLine(house);
                }
                soln.GetNext();
            }
            if (unsolved)
            {
                System.Console.WriteLine("No solution found.");
            }
            else
            {
                System.Console.WriteLine("Solution should have the Norwegian drinking water and the Japanese with the zebra.");
            }
        }
コード例 #7
0
        ///// <summary> Helper function for reading the Bus Driver data file
        ///// </summary>
        //static List<string> Numerals(string line)
        //{
        //    List<string> result = new List<string>();
        //    int left = 0;
        //    while (left < line.Length)
        //    {
        //        char c = line[left];
        //        if (('0' <= c) && (c <= '9'))
        //        {
        //            int right = left + 1;
        //            while ((right < line.Length) && ('0' <= line[right]) && (line[right] <= '9'))
        //                right++;
        //            result.Add(line.Substring(left, right - left));
        //            left = right + 1;
        //        }
        //        else
        //            left++;
        //    }
        //    return result;
        //}

        /// <summary> Bus Drivers.  Data taken from data files of London bus companies, with the
        ///           problem being to find the cheapest, complete, non-overlapping set of task
        ///           assignments that will give a feasible schedule.
        /// </summary>
        public static void BusDrivers(string sourceFilePath)
        {
            // http://www-old.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob022/index.html

            ConstraintSystem S = ConstraintSystem.CreateSolver();

            List <CspTerm> driverCosts  = new List <CspTerm>();
            List <int[]>   driversTasks = new List <int[]>();
            int            nTasks       = 0;

            // Read the data file.  Each row specifies a driver cost, a count of tasks, and the task numbers

            try
            {
                using (StreamReader sr = new StreamReader(sourceFilePath))
                {
                    String line;

                    while ((line = sr.ReadLine()) != null)
                    {
                        int[] tasks;
                        driverCosts.Add(S.Constant(ReadDriver(line, out tasks)));
                        nTasks += tasks.Length;
                        Array.Sort <int>(tasks);
                        driversTasks.Add(tasks);
                    }
                }
                int nDrivers = driversTasks.Count;

                // create a master list of tasks by sorting the raw union and then compressing out duplicates.

                int[] allTasks = new int[nTasks];
                nTasks = 0;
                foreach (int[] tasks in driversTasks)
                {
                    foreach (int x in tasks)
                    {
                        allTasks[nTasks++] = x;
                    }
                }
                Array.Sort <int>(allTasks);
                nTasks = 0;
                for (int i = 1; i < allTasks.Length; i++)
                {
                    if (allTasks[nTasks] < allTasks[i])
                    {
                        allTasks[++nTasks] = allTasks[i];
                    }
                }
                nTasks++;
                Array.Resize <int>(ref allTasks, nTasks);

                // We now have an array of all the tasks, and a list of all the drivers.

                // The problem statement comes down to:
                //    - each task must be assigned exactly once
                //    - minimize the cost of drivers

                // We add a boolean vector representing the drivers, true if the driver is to be used.

                CspTerm[] driversUsed = S.CreateBooleanVector("drivers", nDrivers);   // these are the Decision Variables

                //  We now create an array which maps which tasks are in which drivers.
                //  In addition to this static map, we create a dynamic map of the usage and the costs.

                CspTerm[][] taskActualUse    = new CspTerm[nTasks][];
                CspTerm[]   driverActualCost = new CspTerm[nDrivers];
                for (int t = 0; t < nTasks; t++)
                {
                    taskActualUse[t] = new CspTerm[nDrivers];
                    for (int r = 0; r < nDrivers; r++)
                    {
                        taskActualUse[t][r] = (0 <= Array.BinarySearch <int>(driversTasks[r], allTasks[t])) ? driversUsed[r] : S.False;
                    }
                    S.AddConstraints(
                        S.ExactlyMofN(1, taskActualUse[t])  // this task appears exactly once
                        );
                }

                // set the goal

                for (int r = 0; r < nDrivers; r++)
                {
                    driverActualCost[r] = driversUsed[r] * driverCosts[r];   // dynamic cost map
                }
                S.TryAddMinimizationGoals(S.Sum(driverActualCost));

                // now run the Solver and print the solutions

                int solnId = 0;
                ConstraintSolverSolution soln = S.Solve();
                if (soln.HasFoundSolution)
                {
                    System.Console.WriteLine("Solution #" + solnId++);
                    for (int d = 0; d < driversUsed.Length; d++)
                    {
                        object isUsed;
                        if (!soln.TryGetValue(driversUsed[d], out isUsed))
                        {
                            throw new InvalidProgramException("can't find drive in the solution: " + d.ToString());
                        }

                        // Take only the decision variables which turn out true.
                        // For each true row, print the row number and the list of tasks.

                        if (1 == (int)isUsed)
                        {
                            StringBuilder line = new StringBuilder(d.ToString());
                            line.Append(": ");
                            foreach (int x in driversTasks[d])
                            {
                                line.Append(x.ToString()).Append(", ");
                            }
                            System.Console.WriteLine(line.ToString());
                        }
                    }
                }
                if (solnId == 0)
                {
                    System.Console.WriteLine("No solution found.");
                }
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }
        }
コード例 #8
0
        public static void Run(string parameters)
        {
            // http://www-old.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob022/index.html

            ConstraintSystem S = ConstraintSystem.CreateSolver();

            List <CspTerm> driverCosts  = new List <CspTerm>();
            List <int[]>   driversTasks = new List <int[]>();
            int            nTasks       = 0;

            // Read the data file.  Each row specifies a driver cost, a count of tasks, and the task numbers
            try
            {
                //parse parameters to extract data
                //<line no> = bus driver no-1 (eg line 1 = driver0)
                //1 4 3 4 16 17
                //1 = cost of driver
                // 4 = number of bus routes (tasks)
                // 3, 4, 16, 17 = the bus routes (tasks) for the driver
                var lines = Regex.Split(parameters, "\r\n|\r|\n");
                foreach (var line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line))
                    {
                        continue;
                    }

                    int[] tasks;
                    driverCosts.Add(S.Constant(ReadDriver(line, out tasks)));
                    nTasks += tasks.Length;
                    Array.Sort <int>(tasks);
                    driversTasks.Add(tasks);
                }
                int nDrivers = driversTasks.Count;

                // create a master list of unique tasks (bus routes) that be assigned to drivers
                List <int> tasksU = new List <int>();
                driversTasks.ForEach(x => tasksU.AddRange(x));
                int[] allTasks = tasksU.OrderBy(x => x).Distinct().ToArray();
                nTasks = allTasks.Length;


                // We now have an array of all the tasks, and a list of all the drivers.

                // The problem statement comes down to:
                //    - each task must be assigned exactly once
                //    - minimize the cost of drivers

                // We add a boolean vector representing the drivers, true if the driver is to be used.

                CspTerm[] driversUsed = S.CreateBooleanVector("drivers", nDrivers);   // these are the Decision Variables

                //  We now create an array which maps which tasks are in which drivers.
                //  In addition to this static map, we create a dynamic map of the usage and the costs.
                CspTerm[][] taskActualUse    = new CspTerm[nTasks][];
                CspTerm[]   driverActualCost = new CspTerm[nDrivers];
                for (int t = 0; t < nTasks; t++)              //for each task / bus route
                {
                    taskActualUse[t] = new CspTerm[nDrivers]; //for each route, array of all bus drivers (used to flag who may drive the route)
                    for (int r = 0; r < nDrivers; r++)        //for each driver
                    {
                        taskActualUse[t][r] = (0 <= Array.BinarySearch <int>(driversTasks[r], allTasks[t])) ? driversUsed[r] : S.False;
                    }
                    S.AddConstraints(
                        S.ExactlyMofN(1, taskActualUse[t])  // this task appears exactly once
                        );
                }

                // set the goal: minimize total driver's cost
                for (int r = 0; r < nDrivers; r++)
                {
                    driverActualCost[r] = driversUsed[r] * driverCosts[r];   // dynamic cost map
                }
                S.TryAddMinimizationGoals(S.Sum(driverActualCost));

                // now run the Solver and print the solutions
                int solnId = 0;
                ConstraintSolverSolution soln = S.Solve();
                if (soln.HasFoundSolution)
                {
                    System.Console.WriteLine("Solution #" + solnId++);
                    for (int d = 0; d < driversUsed.Length; d++)
                    {
                        object isUsed;
                        if (!soln.TryGetValue(driversUsed[d], out isUsed))
                        {
                            throw new InvalidProgramException("can't find drive in the solution: " + d.ToString());
                        }

                        // Take only the decision variables which turn out true.
                        // For each true row, print the row number and the list of tasks.

                        if (1 == (int)isUsed)
                        {
                            StringBuilder line = new StringBuilder(d.ToString());
                            line.Append(": ");
                            foreach (int x in driversTasks[d])
                            {
                                line.Append(x.ToString()).Append(", ");
                            }
                            System.Console.WriteLine(line.ToString());
                        }
                    }
                }
                if (solnId == 0)
                {
                    System.Console.WriteLine("No solution found.");
                }
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }
        }