/** * * Discrete tomography * * Problem from http://eclipse.crosscoreop.com/examples/tomo.ecl.txt * """ * This is a little 'tomography' problem, taken from an old issue * of Scientific American. * * A matrix which contains zeroes and ones gets "x-rayed" vertically and * horizontally, giving the total number of ones in each row and column. * The problem is to reconstruct the contents of the matrix from this * information. Sample run: * * ?- go. * 0 0 7 1 6 3 4 5 2 7 0 0 * 0 * 0 * 8 * * * * * * * * * 2 * * * 6 * * * * * * * 4 * * * * * 5 * * * * * * 3 * * * * 7 * * * * * * * * 0 * 0 * * Eclipse solution by Joachim Schimpf, IC-Parc * """ * * See http://www.hakank.org/or-tools/discrete_tomography.py * */ private static void Solve(int[] rowsums, int[] colsums) { Solver solver = new Solver("DiscreteTomography"); // // Data // int r = rowsums.Length; int c = colsums.Length; Console.Write("rowsums: "); for(int i = 0; i < r; i++) { Console.Write(rowsums[i] + " "); } Console.Write("\ncolsums: "); for(int j = 0; j < c; j++) { Console.Write(colsums[j] + " "); } Console.WriteLine("\n"); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(r, c, 0, 1, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // row sums for(int i = 0; i < r; i++) { var tmp = from j in Enumerable.Range(0, c) select x[i,j]; solver.Add(tmp.ToArray().Sum() == rowsums[i]); } // cols sums for(int j = 0; j < c; j++) { var tmp = from i in Enumerable.Range(0, r) select x[i,j]; solver.Add(tmp.ToArray().Sum() == colsums[j]); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < r; i++) { for(int j = 0; j < c; j++) { Console.Write("{0} ", x[i,j].Value() == 1 ? "#" : "." ); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
private static long Solve(long num_buses_check = 0) { SolverParameters sPrm = new SolverParameters(); sPrm.compress_trail = 0; sPrm.trace_level = 0; sPrm.profile_level = 0; Solver solver = new Solver("OrTools",sPrm); //this works // IntVar[,] x = solver.MakeIntVarMatrix(2,2, new int[] {-2,0,1,2}, "x"); //this doesn't work IntVar[,] x = solver.MakeIntVarMatrix(2, 2, new int[] { 0, 1, 2 }, "x"); for (int w = 0; w < 2; w++) { IntVar[] b = new IntVar[2]; for (int i = 0; i < 2; i++) { b[i] = solver.MakeIsEqualCstVar(x[w, i], 0); } solver.Add(solver.MakeSumGreaterOrEqual(b, 2)); } IntVar[] x_flat = x.Flatten(); DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { Console.WriteLine("x: "); for (int j = 0; j < 2; j++) { Console.Write("worker" + (j + 1).ToString() + ":"); for (int i = 0; i < 2; i++) { Console.Write(" {0,2} ", x[j, i].Value()); } Console.Write("\n"); } Console.WriteLine("End at---->" + DateTime.Now); } 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(); return 1; }
/** * * Costas array * * From http://mathworld.wolfram.com/CostasArray.html: * """ * An order-n Costas array is a permutation on {1,...,n} such * that the distances in each row of the triangular difference * table are distinct. For example, the permutation {1,3,4,2,5} * has triangular difference table {2,1,-2,3}, {3,-1,1}, {1,2}, * and {4}. Since each row contains no duplications, the permutation * is therefore a Costas array. * """ * * Also see * http://en.wikipedia.org/wiki/Costas_array * http://hakank.org/or-tools/costas_array.py * */ private static void Solve(int n = 6) { Solver solver = new Solver("CostasArray"); // // Data // Console.WriteLine("n: {0}", n); // // Decision variables // IntVar[] costas = solver.MakeIntVarArray(n, 1, n, "costas"); IntVar[,] differences = solver.MakeIntVarMatrix(n, n, -n+1, n-1, "differences"); // // Constraints // // Fix the values in the lower triangle in the // difference matrix to -n+1. This removes variants // of the difference matrix for the the same Costas array. for(int i = 0; i < n; i++) { for(int j = 0; j <= i; j++ ) { solver.Add(differences[i,j] == -n+1); } } // hakank: All the following constraints are from // Barry O'Sullivans's original model. // solver.Add(costas.AllDifferent()); // "How do the positions in the Costas array relate // to the elements of the distance triangle." for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (i < j) { solver.Add( differences[i,j] - (costas[j] - costas[j-i-1]) == 0); } } } // "All entries in a particular row of the difference // triangle must be distint." for(int i = 0; i < n-2; i++) { IntVar[] tmp = ( from j in Enumerable.Range(0, n) where j > i select differences[i,j]).ToArray(); solver.Add(tmp.AllDifferent()); } // // "All the following are redundant - only here to speed up search." // // "We can never place a 'token' in the same row as any other." for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (i < j) { solver.Add(differences[i,j] != 0); solver.Add(differences[i,j] != 0); } } } for(int k = 2; k < n; k++) { for(int l = 2; l < n; l++) { if (k < l) { solver.Add( (differences[k-2,l-1] + differences[k,l]) - (differences[k-1,l-1] + differences[k-1,l]) == 0 ); } } } // // Search // DecisionBuilder db = solver.MakePhase(costas, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { Console.Write("costas: "); for(int i = 0; i < n; i++) { Console.Write("{0} ", costas[i].Value()); } Console.WriteLine("\ndifferences:"); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { long v = differences[i,j].Value(); if (v == -n+1) { Console.Write(" "); } else { Console.Write("{0,2} ", v); } } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Solves a Sudoku problem. * * This is a very simple 4x4 problem instance: * Problem 26: Shidoku from * "Taking Sudoku Seriously", page 61 * 4 _ _ _ * 3 1 _ _ * * _ _ 4 1 * _ _ _ 2 * */ private static void Solve() { Solver solver = new Solver("Sudoku"); // // data // int block_size = 2; IEnumerable<int> BLOCK = Enumerable.Range(0, block_size); int n = block_size * block_size; IEnumerable<int> RANGE = Enumerable.Range(0, n); // 0 marks an unknown value int[,] initial_grid = {{4, 0, 0, 0}, {3, 1, 0, 0}, {0, 0, 4, 1}, {0, 0, 0, 2}}; // // Decision variables // IntVar[,] grid = solver.MakeIntVarMatrix(n, n, 1, n, "grid"); IntVar[] grid_flat = grid.Flatten(); // // Constraints // // init foreach(int i in RANGE) { foreach(int j in RANGE) { if (initial_grid[i,j] > 0) { solver.Add(grid[i,j] == initial_grid[i,j]); } } } foreach(int i in RANGE) { // rows solver.Add( (from j in RANGE select grid[i,j]).ToArray().AllDifferent()); // cols solver.Add( (from j in RANGE select grid[j,i]).ToArray().AllDifferent()); } // blocks foreach(int i in BLOCK) { foreach(int j in BLOCK) { solver.Add( (from di in BLOCK from dj in BLOCK select grid[i*block_size+di, j*block_size+dj] ).ToArray().AllDifferent()); } } // // Search // DecisionBuilder db = solver.MakePhase(grid_flat, Solver.INT_VAR_SIMPLE, Solver.INT_VALUE_SIMPLE); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++){ Console.Write("{0} ", grid[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Implements the Who killed Agatha problem. * See http://www.hakank.org/google_or_tools/who_killed_agatha.py * */ private static void Solve() { Solver solver = new Solver("WhoKilledAgatha"); int n = 3; int agatha = 0; int butler = 1; int charles = 2; // // Decision variables // IntVar the_killer = solver.MakeIntVar(0, 2, "the_killer"); IntVar the_victim = solver.MakeIntVar(0, 2, "the_victim"); IntVar[,] hates = solver.MakeIntVarMatrix(n, n, 0, 1, "hates"); IntVar[] hates_flat = hates.Flatten(); IntVar[,] richer = solver.MakeIntVarMatrix(n, n, 0, 1, "richer"); IntVar[] richer_flat = richer.Flatten(); IntVar[] all = new IntVar[2 * n * n]; // for branching for(int i = 0; i < n*n; i++) { all[i] = hates_flat[i]; all[(n*n)+i] = richer_flat[i]; } // // Constraints // // Agatha, the butler, and Charles live in Dreadsbury Mansion, and // are the only ones to live there. // A killer always hates, and is no richer than his victim. // hates[the_killer, the_victim] == 1 // hates_flat[the_killer * n + the_victim] == 1 solver.Add(hates_flat.Element(the_killer * n + the_victim) == 1); // richer[the_killer, the_victim] == 0 solver.Add(richer_flat.Element(the_killer * n + the_victim) == 0); // define the concept of richer: // no one is richer than him-/herself... for(int i = 0; i < n; i++) { solver.Add(richer[i,i] == 0); } // (contd...) if i is richer than j then j is not richer than i // if (i != j) => // ((richer[i,j] = 1) <=> (richer[j,i] = 0)) for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (i != j) { solver.Add((richer[i, j]==1) - (richer[j, i]==0) == 0); } } } // Charles hates no one that Agatha hates. // forall i in 0..2: // (hates[agatha, i] = 1) => (hates[charles, i] = 0) for(int i = 0; i < n; i++) { solver.Add((hates[agatha,i]==1) - (hates[charles,i]==0) <= 0); } // Agatha hates everybody except the butler. solver.Add(hates[agatha,charles] == 1); solver.Add(hates[agatha,agatha] == 1); solver.Add(hates[agatha,butler] == 0); // The butler hates everyone not richer than Aunt Agatha. // forall i in 0..2: // (richer[i, agatha] = 0) => (hates[butler, i] = 1) for(int i = 0; i < n; i++) { solver.Add((richer[i,agatha] == 0)-(hates[butler,i] == 1)<=0); } // The butler hates everyone whom Agatha hates. // forall i : 0..2: // (hates[agatha, i] = 1) => (hates[butler, i] = 1) for(int i = 0; i < n; i++) { solver.Add((hates[agatha,i] == 1)-(hates[butler,i] == 1)<=0); } // Noone hates everyone. // forall i in 0..2: // (sum j in 0..2: hates[i,j]) <= 2 for(int i = 0; i < n; i++) { solver.Add((from j in Enumerable.Range(0, n) select hates[i,j] ).ToArray().Sum() <= 2 ); } // Who killed Agatha? solver.Add(the_victim == agatha); // // Search // DecisionBuilder db = solver.MakePhase(all, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { Console.WriteLine("the_killer: " + the_killer.Value()); } 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(); }
/** * * 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(); }
/** * * Nontransitive dice. * * From * http://en.wikipedia.org/wiki/Nontransitive_dice * """ * A set of nontransitive dice is a set of dice for which the relation * 'is more likely to roll a higher number' is not transitive. See also * intransitivity. * * This situation is similar to that in the game Rock, Paper, Scissors, * in which each element has an advantage over one choice and a * disadvantage to the other. * """ * * Also see http://www.hakank.org/or-tools/nontransitive_dice.py * * */ private static void Solve(int m=3, int n=6, int minimize_val=0) { Solver solver = new Solver("Nontransitive_dice"); Console.WriteLine("Number of dice: {0}", m); Console.WriteLine("Number of sides: {0}", n); Console.WriteLine("minimize_val: {0}\n", minimize_val); // // Decision variables // // The dice IntVar[,] dice = solver.MakeIntVarMatrix(m, n, 1, n*2, "dice"); IntVar[] dice_flat = dice.Flatten(); // For comparison (probability) IntVar[,] comp = solver.MakeIntVarMatrix(m, 2, 0, n*n, "dice"); IntVar[] comp_flat = comp.Flatten(); // For branching IntVar[] all = dice_flat.Concat(comp_flat).ToArray(); // The following variables are for summaries or objectives IntVar[] gap = solver.MakeIntVarArray(m, 0, n*n, "gap"); IntVar gap_sum = gap.Sum().Var(); IntVar max_val = dice_flat.Max().Var(); IntVar max_win = comp_flat.Max().Var(); // number of occurrences of each value of the dice IntVar[] counts = solver.MakeIntVarArray(n*2+1, 0, n*m, "counts"); // // Constraints // // Number of occurrences for each number solver.Add(dice_flat.Distribute(counts)); // Order of the number of each die, lowest first for(int i = 0; i < m; i++) { for(int j = 0; j < n-1; j++) { solver.Add(dice[i,j] <= dice[i,j+1]); } } // Nontransitivity for(int i = 0; i < m; i++) { solver.Add(comp[i,0] > comp[i,1]); } // Probability gap for(int i = 0; i < m; i++) { solver.Add(gap[i] == comp[i,0] - comp[i,1]); solver.Add(gap[i] > 0); } // And now we roll... // comp[] is the number of wins for [A vs B, B vs A] for(int d = 0; d < m; d++) { IntVar sum1 = ( from r1 in Enumerable.Range(0, n) from r2 in Enumerable.Range(0, n) select (dice[d % m, r1] > dice[(d+1) % m, r2]) ).ToArray().Sum().Var(); solver.Add(comp[d%m,0] == sum1); IntVar sum2 = ( from r1 in Enumerable.Range(0, n) from r2 in Enumerable.Range(0, n) select (dice[(d+1) % m, r1] > dice[d % m, r2]) ).ToArray().Sum().Var(); solver.Add(comp[d%m,1] == sum2); } // // Search // DecisionBuilder db = solver.MakePhase(all, Solver.INT_VAR_DEFAULT, Solver.ASSIGN_MIN_VALUE); if (minimize_val > 0) { Console.WriteLine("Minimizing max_val"); OptimizeVar obj = max_val.Minimize(1); // Other experiments: // OptimizeVar obj = max_win.Maximize(1); // OptimizeVar obj = gap_sum.Maximize(1); solver.NewSearch(db, obj); } else { solver.NewSearch(db); } while (solver.NextSolution()) { Console.WriteLine("gap_sum: {0}", gap_sum.Value()); Console.WriteLine("gap: {0}", (from i in Enumerable.Range(0, m) select gap[i].Value().ToString() ).ToArray() ); Console.WriteLine("max_val: {0}", max_val.Value()); Console.WriteLine("max_win: {0}", max_win.Value()); Console.WriteLine("dice:"); for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { Console.Write(dice[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine("comp:"); for(int i = 0; i < m; i++) { for(int j = 0; j < 2; j++) { Console.Write(comp[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine("counts:"); for(int i = 1; i < n*2+1; i++) { int c = (int)counts[i].Value(); if (c > 0) { Console.Write("{0}({1}) ", i, c); } } Console.WriteLine("\n"); } 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(); }
/** * * Word square. * * From http://en.wikipedia.org/wiki/Word_square * """ * A word square is a special case of acrostic. It consists of a set of words, * all having the same number of letters as the total number of words (the * 'order' of the square); when the words are written out in a square grid * horizontally, the same set of words can be read vertically. * """ * * See http://www.hakank.org/or-tools/word_square.py * */ private static void Solve(String[] words, int word_len, int num_answers) { Solver solver = new Solver("WordSquare"); int num_words = words.Length; Console.WriteLine("num_words: " + num_words); int n = word_len; IEnumerable<int> WORDLEN = Enumerable.Range(0, word_len); // // convert a character to integer // String alpha = "abcdefghijklmnopqrstuvwxyz"; Hashtable d = new Hashtable(); Hashtable rev = new Hashtable(); int count = 1; for(int a = 0; a < alpha.Length; a++) { d[alpha[a]] = count; rev[count] = a; count++; } int num_letters = alpha.Length; // // Decision variables // IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, 0, num_letters, "A"); IntVar[] A_flat = A.Flatten(); IntVar[] E = solver.MakeIntVarArray(n, 0, num_words, "E"); // // Constraints // solver.Add(E.AllDifferent()); // copy the words to a matrix for(int i = 0; i < num_words; i++) { char[] s = words[i].ToArray(); foreach(int j in WORDLEN) { int t = (int)d[s[j]]; solver.Add(A[i,j] == t); } } foreach(int i in WORDLEN) { foreach(int j in WORDLEN) { solver.Add(A_flat.Element(E[i]*word_len+j) == A_flat.Element(E[j]*word_len+i)); } } // // Search // DecisionBuilder db = solver.MakePhase(E.Concat(A_flat).ToArray(), Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); int num_sols = 0; while (solver.NextSolution()) { num_sols++; for(int i = 0; i < n; i++) { Console.WriteLine(words[E[i].Value()] + " "); } Console.WriteLine(); if (num_answers > 0 && num_sols >= num_answers) { 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(); }
/** * * Solves the Magic Square problem. * See http://www.hakank.org/or-tools/magic_square.py * */ private static void Solve(int n = 4, int num = 0, int print = 1) { Solver solver = new Solver("MagicSquare"); Console.WriteLine("n: {0}", n); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n*n, "x"); // for the branching IntVar[] x_flat = x.Flatten(); // // Constraints // long s = (n * (n * n + 1)) / 2; Console.WriteLine("s: " + s); IntVar[] diag1 = new IntVar[n]; IntVar[] diag2 = new IntVar[n]; for(int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; for(int j = 0; j < n; j++) { row[j] = x[i,j]; } // sum row to s solver.Add(row.Sum() == s); diag1[i] = x[i,i]; diag2[i] = x[i,n - i - 1]; } // sum diagonals to s solver.Add(diag1.Sum() == s); solver.Add(diag2.Sum() == s); // sum columns to s for(int j = 0; j < n; j++) { IntVar[] col = new IntVar[n]; for(int i = 0; i < n; i++) { col[i] = x[i,j]; } solver.Add(col.Sum() == s); } // all are different solver.Add(x_flat.AllDifferent()); // symmetry breaking: upper left is 1 // solver.Add(x[0,0] == 1); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_CENTER_VALUE); solver.NewSearch(db); int c = 0; while (solver.NextSolution()) { if (print != 0) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(x[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } c++; if (num > 0 && c >= num) { 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(); }
/** * * Killer Sudoku. * * http://en.wikipedia.org/wiki/Killer_Sudoku * """ * Killer sudoku (also killer su doku, sumdoku, sum doku, addoku, or * samunamupure) is a puzzle that combines elements of sudoku and kakuro. * Despite the name, the simpler killer sudokus can be easier to solve * than regular sudokus, depending on the solver's skill at mental arithmetic; * the hardest ones, however, can take hours to crack. * * ... * * The objective is to fill the grid with numbers from 1 to 9 in a way that * the following conditions are met: * * - Each row, column, and nonet contains each number exactly once. * - The sum of all numbers in a cage must match the small number printed * in its corner. * - No number appears more than once in a cage. (This is the standard rule * for killer sudokus, and implies that no cage can include more * than 9 cells.) * * In 'Killer X', an additional rule is that each of the long diagonals * contains each number once. * """ * * Here we solve the problem from the Wikipedia page, also shown here * http://en.wikipedia.org/wiki/File:Killersudoku_color.svg * * The output is: * 2 1 5 6 4 7 3 9 8 * 3 6 8 9 5 2 1 7 4 * 7 9 4 3 8 1 6 5 2 * 5 8 6 2 7 4 9 3 1 * 1 4 2 5 9 3 8 6 7 * 9 7 3 8 1 6 4 2 5 * 8 2 1 7 3 9 5 4 6 * 6 5 9 4 2 8 7 1 3 * 4 3 7 1 6 5 2 8 9 * * Also see http://www.hakank.org/or-tools/killer_sudoku.py * though this C# model has another representation of * the problem instance. * */ private static void Solve() { Solver solver = new Solver("KillerSudoku"); // size of matrix int cell_size = 3; IEnumerable<int> CELL = Enumerable.Range(0, cell_size); int n = cell_size*cell_size; IEnumerable<int> RANGE = Enumerable.Range(0, n); // For a better view of the problem, see // http://en.wikipedia.org/wiki/File:Killersudoku_color.svg // hints // sum, the hints // Note: this is 1-based int[][] problem = { new int[] { 3, 1,1, 1,2}, new int[] {15, 1,3, 1,4, 1,5}, new int[] {22, 1,6, 2,5, 2,6, 3,5}, new int[] {4, 1,7, 2,7}, new int[] {16, 1,8, 2,8}, new int[] {15, 1,9, 2,9, 3,9, 4,9}, new int[] {25, 2,1, 2,2, 3,1, 3,2}, new int[] {17, 2,3, 2,4}, new int[] { 9, 3,3, 3,4, 4,4}, new int[] { 8, 3,6, 4,6, 5,6}, new int[] {20, 3,7, 3,8, 4,7}, new int[] { 6, 4,1, 5,1}, new int[] {14, 4,2, 4,3}, new int[] {17, 4,5, 5,5, 6,5}, new int[] {17, 4,8, 5,7, 5,8}, new int[] {13, 5,2, 5,3, 6,2}, new int[] {20, 5,4, 6,4, 7,4}, new int[] {12, 5,9, 6,9}, new int[] {27, 6,1, 7,1, 8,1, 9,1}, new int[] { 6, 6,3, 7,2, 7,3}, new int[] {20, 6,6, 7,6, 7,7}, new int[] { 6, 6,7, 6,8}, new int[] {10, 7,5, 8,4, 8,5, 9,4}, new int[] {14, 7,8, 7,9, 8,8, 8,9}, new int[] { 8, 8,2, 9,2}, new int[] {16, 8,3, 9,3}, new int[] {15, 8,6, 8,7}, new int[] {13, 9,5, 9,6, 9,7}, new int[] {17, 9,8, 9,9} }; int num_p = 29; // Number of segments // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // // The first three constraints is the same as for sudokus.cs // // alldifferent rows and columns foreach(int i in RANGE) { // rows solver.Add( (from j in RANGE select x[i,j]).ToArray().AllDifferent()); // cols solver.Add( (from j in RANGE select x[j,i]).ToArray().AllDifferent()); } // cells foreach(int i in CELL) { foreach(int j in CELL) { solver.Add( (from di in CELL from dj in CELL select x[i*cell_size+di, j*cell_size+dj] ).ToArray().AllDifferent()); } } // Sum the segments and ensure alldifferent for(int i = 0; i < num_p; i++) { int[] segment = problem[i]; // Remove the sum from the segment int[] s2 = new int[segment.Length-1]; for(int j = 1; j < segment.Length; j++) { s2[j-1] = segment[j]; } // sum this segment calc(solver, s2, x, segment[0]); // all numbers in this segment must be distinct int len = segment.Length / 2; solver.Add( (from j in Enumerable.Range(0, len) select x[s2[j*2]-1, s2[j*2+1]-1]) .ToArray().AllDifferent()); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { int v = (int)x[i,j].Value(); if (v > 0) { Console.Write(v + " "); } else { Console.Write(" "); } } Console.WriteLine(); } } 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(); }
/** * * Nurse rostering * * This is a simple nurse rostering model using a DFA and * the built-in TransitionConstraint. * * The DFA is from MiniZinc Tutorial, Nurse Rostering example: * - one day off every 4 days * - no 3 nights in a row. * * Also see: * - http://www.hakank.org/or-tools/nurse_rostering.py * - http://www.hakank.org/or-tools/nurse_rostering_regular.cs * which use (a decomposition of) regular constraint * */ private static void Solve(int nurse_multiplier, int week_multiplier) { Console.WriteLine("Starting Nurse Rostering"); Console.WriteLine(" - {0} teams of 7 nurses", nurse_multiplier); Console.WriteLine(" - {0} blocks of 14 days", week_multiplier); Solver solver = new Solver("NurseRostering"); // // Data // // Note: If you change num_nurses or num_days, // please also change the constraints // on nurse_stat and/or day_stat. int num_nurses = 7 * nurse_multiplier; int num_days = 14 * week_multiplier; // Note: I had to add a dummy shift. int dummy_shift = 0; int day_shift = 1; int night_shift = 2; int off_shift = 3; int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; int[] valid_shifts = {day_shift, night_shift, off_shift}; // the DFA (for regular) int initial_state = 1; int[] accepting_states = {1,2,3,4,5,6}; /* // This is the transition function // used in nurse_rostering_regular.cs int[,] transition_fn = { // d,n,o {2,3,1}, // state 1 {4,4,1}, // state 2 {4,5,1}, // state 3 {6,6,1}, // state 4 {6,0,1}, // state 5 {0,0,1} // state 6 }; */ // For TransitionConstraint IntTupleSet transition_tuples = new IntTupleSet(3); // state, input, next state transition_tuples.InsertAll(new int[,] { {1,1,2}, {1,2,3}, {1,3,1}, {2,1,4}, {2,2,4}, {2,3,1}, {3,1,4}, {3,2,5}, {3,3,1}, {4,1,6}, {4,2,6}, {4,3,1}, {5,1,6}, {5,3,1}, {6,3,1} }); string[] days = {"d","n","o"}; // for presentation // // Decision variables // // // For TransitionConstraint // IntVar[,] x = solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); IntVar[] x_flat = x.Flatten(); // // summary of the nurses // IntVar[] nurse_stat = new IntVar[num_nurses]; // // summary of the shifts per day // int num_shifts = shifts.Length; IntVar[,] day_stat = new IntVar[num_days, num_shifts]; for(int i = 0; i < num_days; i++) { for(int j = 0; j < num_shifts; j++) { day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); } } // // Constraints // for(int i = 0; i < num_nurses; i++) { IntVar[] reg_input = new IntVar[num_days]; for(int j = 0; j < num_days; j++) { reg_input[j] = x[i,j]; } solver.Add(reg_input.Transition(transition_tuples, initial_state, accepting_states)); } // // Statistics and constraints for each nurse // for(int nurse = 0; nurse < num_nurses; nurse++) { // Number of worked days (either day or night shift) IntVar[] nurse_days = new IntVar[num_days]; for(int day = 0; day < num_days; day++) { nurse_days[day] = x[nurse, day].IsMember(new int[] { day_shift, night_shift }); } nurse_stat[nurse] = nurse_days.Sum().Var(); // Each nurse must work between 7 and 10 // days/nights during this period solver.Add(nurse_stat[nurse] >= 7 * week_multiplier / nurse_multiplier); solver.Add(nurse_stat[nurse] <= 10 * week_multiplier / nurse_multiplier); } // // Statistics and constraints for each day // for(int day = 0; day < num_days; day++) { IntVar[] nurses = new IntVar[num_nurses]; for(int nurse = 0; nurse < num_nurses; nurse++) { nurses[nurse] = x[nurse, day]; } IntVar[] stats = new IntVar[num_shifts]; for (int shift = 0; shift < num_shifts; ++shift) { stats[shift] = day_stat[day, shift]; } solver.Add(nurses.Distribute(stats)); // // Some constraints for each day: // // Note: We have a strict requirements of // the number of shifts. // Using atleast constraints is harder // in this model. // if (day % 7 == 5 || day % 7 == 6) { // special constraints for the weekends solver.Add(day_stat[day, day_shift] == 2 * nurse_multiplier); solver.Add(day_stat[day, night_shift] == nurse_multiplier); solver.Add(day_stat[day, off_shift] == 4 * nurse_multiplier); } else { // for workdays: // - exactly 3 on day shift solver.Add(day_stat[day, day_shift] == 3 * nurse_multiplier); // - exactly 2 on night solver.Add(day_stat[day, night_shift] == 2 * nurse_multiplier); // - exactly 2 off duty solver.Add(day_stat[day, off_shift] == 2 * nurse_multiplier); } } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); SearchMonitor log = solver.MakeSearchLog(1000000); solver.NewSearch(db, log); int num_solutions = 0; while (solver.NextSolution()) { num_solutions++; for(int i = 0; i < num_nurses; i++) { Console.Write("Nurse #{0,-2}: ", i); var occ = new Dictionary<int, int>(); for(int j = 0; j < num_days; j++) { int v = (int)x[i,j].Value()-1; if (!occ.ContainsKey(v)) { occ[v] = 0; } occ[v]++; Console.Write(days[v] + " "); } Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); foreach(int s in valid_shifts) { int v = 0; if (occ.ContainsKey(s-1)) { v = occ[s-1]; } Console.Write(" {0}:{1}", days[s-1], v); } Console.WriteLine(); } Console.WriteLine(); Console.WriteLine("Statistics per day:\nDay d n o"); for(int j = 0; j < num_days; j++) { Console.Write("Day #{0,2}: ", j); foreach(int t in valid_shifts) { Console.Write(day_stat[j,t].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); // We just show 2 solutions if (num_solutions > 1) { 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(); }
/** * * Fill-a-Pix problem * * From http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/basiclogic * """ * Each puzzle consists of a grid containing clues in various places. The * object is to reveal a hidden picture by painting the squares around each * clue so that the number of painted squares, including the square with * the clue, matches the value of the clue. * """ * * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules * """ * Fill-a-Pix is a Minesweeper-like puzzle based on a grid with a pixilated * picture hidden inside. Using logic alone, the solver determines which * squares are painted and which should remain empty until the hidden picture * is completely exposed. * """ * * Fill-a-pix History: * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/history * * Also see http://www.hakank.org/google_or_tools/fill_a_pix.py * * */ private static void Solve() { Solver solver = new Solver("FillAPix"); // // data // int[] S = {-1, 0, 1}; Console.WriteLine("Problem:"); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (puzzle[i,j] > X) { Console.Write(puzzle[i,j] + " "); } else { Console.Write("X "); } } Console.WriteLine(); } Console.WriteLine(); // // Decision variables // IntVar[,] pict = solver.MakeIntVarMatrix(n, n, 0, 1, "pict"); IntVar[] pict_flat = pict.Flatten(); // for branching // // Constraints // for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (puzzle[i,j] > X) { // this cell is the sum of all surrounding cells var tmp = from a in S from b in S where i + a >= 0 && j + b >= 0 && i + a < n && j + b < n select(pict[i+a,j+b]); solver.Add(tmp.ToArray().Sum() == puzzle[i,j]); } } } // // Search // DecisionBuilder db = solver.MakePhase(pict_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); int sol = 0; while (solver.NextSolution()) { sol++; Console.WriteLine("Solution #{0} ", sol + " "); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++){ Console.Write(pict[i,j].Value() == 1 ? "#" : " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Set partition problem. * * Problem formulation from * http://www.koalog.com/resources/samples/PartitionProblem.java.html * """ * This is a partition problem. * Given the set S = {1, 2, ..., n}, * it consists in finding two sets A and B such that: * * A U B = S, * |A| = |B|, * sum(A) = sum(B), * sum_squares(A) = sum_squares(B) * * """ * * This model uses a binary matrix to represent the sets. * * * Also see http://www.hakank.org/or-tools/set_partition.py * */ private static void Solve(int n=16, int num_sets=2) { Solver solver = new Solver("SetPartition"); Console.WriteLine("n: {0}", n); Console.WriteLine("num_sets: {0}", num_sets); IEnumerable<int> Sets = Enumerable.Range(0, num_sets); IEnumerable<int> NRange = Enumerable.Range(0, n); // // Decision variables // IntVar[,] a = solver.MakeIntVarMatrix(num_sets, n, 0, 1, "a"); IntVar[] a_flat = a.Flatten(); // // Constraints // // partition set partition_sets(solver, a, num_sets, n); foreach(int i in Sets) { foreach(int j in Sets) { // same cardinality solver.Add( (from k in NRange select a[i,k]).ToArray().Sum() == (from k in NRange select a[j,k]).ToArray().Sum()); // same sum solver.Add( (from k in NRange select (k*a[i,k])).ToArray().Sum() == (from k in NRange select (k*a[j,k])).ToArray().Sum()); // same sum squared solver.Add( (from k in NRange select (k*a[i,k]*k*a[i,k])).ToArray().Sum() == (from k in NRange select (k*a[j,k]*k*a[j,k])).ToArray().Sum()); } } // symmetry breaking for num_sets == 2 if (num_sets == 2) { solver.Add(a[0,0] == 1); } // // Search // DecisionBuilder db = solver.MakePhase(a_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { int[,] a_val = new int[num_sets, n]; foreach(int i in Sets) { foreach(int j in NRange) { a_val[i,j] = (int)a[i,j].Value(); } } Console.WriteLine("sums: {0}", (from j in NRange select (j+1)*a_val[0,j]).ToArray().Sum()); Console.WriteLine("sums squared: {0}", (from j in NRange select (int)Math.Pow((j+1)*a_val[0,j],2)).ToArray().Sum()); // Show the numbers in each set foreach(int i in Sets) { if ( (from j in NRange select a_val[i,j]).ToArray().Sum() > 0 ) { Console.Write(i+1 + ": "); foreach(int j in NRange) { if (a_val[i,j] == 1) { Console.Write((j+1) + " "); } } Console.WriteLine(); } } Console.WriteLine(); } 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(); }
/** * * Nurse rostering * * This is a simple nurse rostering model using a DFA and * my decomposition of regular constraint. * * The DFA is from MiniZinc Tutorial, Nurse Rostering example: * - one day off every 4 days * - no 3 nights in a row. * * Also see http://www.hakank.org/or-tools/nurse_rostering.py * */ private static void Solve() { Solver solver = new Solver("NurseRostering"); // // Data // // Note: If you change num_nurses or num_days, // please also change the constraints // on nurse_stat and/or day_stat. int num_nurses = 7; int num_days = 14; // Note: I had to add a dummy shift. int dummy_shift = 0; int day_shift = 1; int night_shift = 2; int off_shift = 3; int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; int[] valid_shifts = {day_shift, night_shift, off_shift}; // the DFA (for regular) int n_states = 6; int input_max = 3; int initial_state = 1; // 0 is for the failing state int[] accepting_states = {1,2,3,4,5,6}; int[,] transition_fn = { // d,n,o {2,3,1}, // state 1 {4,4,1}, // state 2 {4,5,1}, // state 3 {6,6,1}, // state 4 {6,0,1}, // state 5 {0,0,1} // state 6 }; string[] days = {"d","n","o"}; // for presentation // // Decision variables // // For regular IntVar[,] x = solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); IntVar[] x_flat = x.Flatten(); // summary of the nurses IntVar[] nurse_stat = solver.MakeIntVarArray(num_nurses, 0, num_days, "nurse_stat"); // summary of the shifts per day int num_shifts = shifts.Length; IntVar[,] day_stat = new IntVar[num_days, num_shifts]; for(int i = 0; i < num_days; i++) { for(int j = 0; j < num_shifts; j++) { day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); } } // // Constraints // for(int i = 0; i < num_nurses; i++) { IntVar[] reg_input = new IntVar[num_days]; for(int j = 0; j < num_days; j++) { reg_input[j] = x[i,j]; } MyRegular(solver, reg_input, n_states, input_max, transition_fn, initial_state, accepting_states); } // // Statistics and constraints for each nurse // for(int i = 0; i < num_nurses; i++) { // Number of worked days (either day or night shift) IntVar[] b = new IntVar[num_days]; for(int j = 0; j < num_days; j++) { b[j] = ((x[i,j] == day_shift) + (x[i,j] == night_shift)).Var(); } solver.Add(b.Sum() == nurse_stat[i]); // Each nurse must work between 7 and 10 // days/nights during this period solver.Add(nurse_stat[i] >= 7); solver.Add(nurse_stat[i] <= 10); } // // Statistics and constraints for each day // for(int j = 0; j < num_days; j++) { for(int t = 0; t < num_shifts; t++) { IntVar[] b = new IntVar[num_nurses]; for(int i = 0; i < num_nurses; i++) { b[i] = x[i,j] == t; } solver.Add(b.Sum() == day_stat[j,t]); } // // Some constraints for each day: // // Note: We have a strict requirements of // the number of shifts. // Using atleast constraints is harder // in this model. // if (j % 7 == 5 || j % 7 == 6) { // special constraints for the weekends solver.Add(day_stat[j,day_shift] == 2); solver.Add(day_stat[j,night_shift] == 1); solver.Add(day_stat[j,off_shift] == 4 ); } else { // for workdays: // - exactly 3 on day shift solver.Add(day_stat[j,day_shift] == 3); // - exactly 2 on night solver.Add(day_stat[j,night_shift] == 2); // - exactly 2 off duty solver.Add(day_stat[j,off_shift] == 2 ); } } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); int num_solutions = 0; while (solver.NextSolution()) { num_solutions++; for(int i = 0; i < num_nurses; i++) { Console.Write("Nurse #{0,-2}: ", i); var occ = new Dictionary<int, int>(); for(int j = 0; j < num_days; j++) { int v = (int)x[i,j].Value()-1; if (!occ.ContainsKey(v)) { occ[v] = 0; } occ[v]++; Console.Write(days[v] + " "); } Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); foreach(int s in valid_shifts) { int v = 0; if (occ.ContainsKey(s-1)) { v = occ[s-1]; } Console.Write(" {0}:{1}", days[s-1], v); } Console.WriteLine(); } Console.WriteLine(); Console.WriteLine("Statistics per day:\nDay d n o"); for(int j = 0; j < num_days; j++) { Console.Write("Day #{0,2}: ", j); foreach(int t in valid_shifts) { Console.Write(day_stat[j,t].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); // We just show 2 solutions if (num_solutions > 1) { 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(); }
/** * * Max flow problem. * * From Winston 'Operations Research', page 420f, 423f * Sunco Oil example. * * * Also see http://www.hakank.org/or-tools/max_flow_winston1.py * */ private static void Solve() { Solver solver = new Solver("MaxFlowWinston1"); // // Data // int n = 5; IEnumerable<int> NODES = Enumerable.Range(0, n); // The arcs // Note: // This is 1-based to be compatible with other implementations. // int[,] arcs1 = { {1, 2}, {1, 3}, {2, 3}, {2, 4}, {3, 5}, {4, 5}, {5, 1} }; // Capacities int [] cap = {2,3,3,4,2,1,100}; // Convert arcs to 0-based int num_arcs = arcs1.GetLength(0); IEnumerable<int> ARCS = Enumerable.Range(0, num_arcs); int[,] arcs = new int[num_arcs, 2]; foreach(int i in ARCS) { for(int j = 0; j < 2; j++) { arcs[i,j] = arcs1[i,j] - 1; } } // Convert arcs to matrix (for sanity checking below) int[,] mat = new int[num_arcs, num_arcs]; foreach(int i in NODES) { foreach(int j in NODES) { int c = 0; foreach(int k in ARCS) { if (arcs[k,0] == i && arcs[k,1] == j) { c = 1; } } mat[i,j] = c; } } // // Decision variables // IntVar[,] flow = solver.MakeIntVarMatrix(n, n, 0, 200, "flow"); IntVar z = flow[n-1, 0].VarWithName("z"); // // Constraints // // capacity of arcs foreach(int i in ARCS) { solver.Add(flow[arcs[i,0], arcs[i,1]] <= cap[i]); } // inflows == outflows foreach(int i in NODES) { var s1 = (from k in ARCS where arcs[k,1] == i select flow[arcs[k,0], arcs[k,1]] ).ToArray().Sum(); var s2 = (from k in ARCS where arcs[k,0] == i select flow[arcs[k,0], arcs[k,1]] ).ToArray().Sum(); solver.Add(s1 == s2); } // Sanity check: just arcs with connections can have a flow. foreach(int i in NODES) { foreach(int j in NODES) { if (mat[i,j] == 0) { solver.Add(flow[i,j] == 0); } } } // // Objective // OptimizeVar obj = z.Maximize(1); // // Search // DecisionBuilder db = solver.MakePhase(flow.Flatten(), Solver.INT_VAR_DEFAULT, Solver.ASSIGN_MAX_VALUE); solver.NewSearch(db, obj); while (solver.NextSolution()) { Console.WriteLine("z: {0}",z.Value()); foreach(int i in NODES) { foreach(int j in NODES) { Console.Write(flow[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * From Programmers Stack Exchange (C#) * http://programmers.stackexchange.com/questions/153184/partitioning-set-into-subsets-with-respect-to-equality-of-sum-among-subsets * Partitioning set into subsets with respect to equality of sum among subsets * """ * let say i have {3, 1, 1, 2, 2, 1,5,2,7} set of numbers, I need to split the * numbers such that sum of subset1 should be equal to sum of subset2 * {3,2,7} {1,1,2,1,5,2}. First we should identify whether we can split number(one * way might be dividable by 2 without any remainder) and if we can, we should * write our algorithm two create s1 and s2 out of s. * * How to proceed with this approach? I read partition problem in wiki and even in some * articles but i am not able to get anything. Can someone help me to find the * right algorithm and its explanation in simple English? * * * Also see http://www.hakank.org/or-tools/set_partition.cs * * * Model by Hakan Kjellerstrand ([email protected]) * See other or-tools/C# models at http://www.hakank.org/or-tools/#csharp * */ private static void Solve(int n = 10, int max = 10, int num_subsets = 2, int sols_to_show = 0) { Solver solver = new Solver("PartitionSets"); Console.WriteLine("n: " + n); Console.WriteLine("max: " + max); Console.WriteLine("num_subsets: " + num_subsets); Console.WriteLine("sols_to_show: " + sols_to_show); // // Data // // int[] s = {3, 1, 1, 2, 2, 1, 5, 2, 7}; // int n = s.Length; int seed = (int)DateTime.Now.Ticks; Random generator = new Random(seed); int[] s = new int[n]; for(int i = 0; i < n; i++) { s[i] = 1 + generator.Next(max); } while (s.Sum() % num_subsets != 0) { int ix = generator.Next(n); Console.WriteLine("The sum of s must be divisible by the number of subsets. Adjusting index " + ix); s[ix] = 1 + generator.Next(max); } Console.WriteLine("\n" + n + " numbers generated\n"); int the_sum = (int)s.Sum() / num_subsets; Console.WriteLine("The sum: " + the_sum); IEnumerable<int> NRange = Enumerable.Range(0, n); IEnumerable<int> Sets = Enumerable.Range(0, num_subsets); // // Decision variables // // To which subset (s) do x[s, i] belong? IntVar[,] x = solver.MakeIntVarMatrix(num_subsets, n, 0, 1, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // Ensure that a number is in exact one subset for(int k = 0; k < n; k++) { solver.Add( (from p in Sets select (x[p,k])).ToArray().Sum() == 1); } // Ensure that the sum of all subsets are the same. // for(int p = 0; p < num_subsets-1; p++) { // solver.Add( // (from k in NRange select (s[k]*x[p,k])).ToArray().Sum() // == // (from k in NRange select (s[k]*x[p+1,k])).ToArray().Sum() // ); // } for(int p = 0; p < num_subsets; p++) { solver.Add( (from k in NRange select (s[k]*x[p,k])).ToArray().Sum() == the_sum ); } // symmetry breaking: assign first number to subset 1 solver.Add(x[0,0] == 1); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); int sols = 0; while (solver.NextSolution()) { sols++; foreach(int i in Sets) { Console.Write("subset " + i + ": "); int sum = 0; foreach(int j in NRange) { if ((int)x[i,j].Value() == 1) { Console.Write(s[j] + " "); sum += s[j]; } } Console.WriteLine(" sum: " + sum); } Console.WriteLine(); if (sols_to_show > 0 && sols >= sols_to_show) { 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(); }
/** * * Solves a Strimko problem. * See http://www.hakank.org/google_or_tools/strimko2.py * */ private static void Solve() { Solver solver = new Solver("Strimko2"); // // data // int[,] streams = {{1,1,2,2,2,2,2}, {1,1,2,3,3,3,2}, {1,4,1,3,3,5,5}, {4,4,3,1,3,5,5}, {4,6,6,6,7,7,5}, {6,4,6,4,5,5,7}, {6,6,4,7,7,7,7}}; // Note: This is 1-based int[,] placed = {{2,1,1}, {2,3,7}, {2,5,6}, {2,7,4}, {3,2,7}, {3,6,1}, {4,1,4}, {4,7,5}, {5,2,2}, {5,6,6}}; int n = streams.GetLength(0); int num_placed = placed.GetLength(0); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // all rows and columns must be unique, i.e. a Latin Square for(int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; IntVar[] col = new IntVar[n]; for(int j = 0; j < n; j++) { row[j] = x[i,j]; col[j] = x[j,i]; } solver.Add(row.AllDifferent()); solver.Add(col.AllDifferent()); } // streams for(int s = 1; s <= n; s++) { IntVar[] tmp = (from i in Enumerable.Range(0, n) from j in Enumerable.Range(0, n) where streams[i,j] == s select x[i,j]).ToArray(); solver.Add(tmp.AllDifferent()); } // placed for(int i = 0; i < num_placed; i++) { // note: also adjust to 0-based solver.Add(x[placed[i,0] - 1,placed[i,1] - 1] == placed[i,2]); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(x[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Futoshiki problem. * * From http://en.wikipedia.org/wiki/Futoshiki * """ * The puzzle is played on a square grid, such as 5 x 5. The objective * is to place the numbers 1 to 5 (or whatever the dimensions are) * such that each row, and column contains each of the digits 1 to 5. * Some digits may be given at the start. In addition, inequality * constraints are also initially specifed between some of the squares, * such that one must be higher or lower than its neighbour. These * constraints must be honoured as the grid is filled out. * """ * * Also see http://www.hakank.org/or-tools/futoshiki.py * */ private static void Solve(int[,] values, int[,] lt) { Solver solver = new Solver("Futoshiki"); int size = values.GetLength(0); IEnumerable<int> RANGE = Enumerable.Range(0, size); IEnumerable<int> NUMQD = Enumerable.Range(0, lt.GetLength(0)); // // Decision variables // IntVar[,] field = solver.MakeIntVarMatrix(size, size, 1, size, "field"); IntVar[] field_flat = field.Flatten(); // // Constraints // // set initial values foreach(int row in RANGE) { foreach(int col in RANGE) { if (values[row,col] > 0) { solver.Add(field[row,col] == values[row,col]); } } } // all rows have to be different foreach(int row in RANGE) { solver.Add((from col in RANGE select field[row,col]).ToArray().AllDifferent()); } // all columns have to be different foreach(int col in RANGE) { solver.Add((from row in RANGE select field[row,col]).ToArray().AllDifferent()); } // all < constraints are satisfied // Also: make 0-based foreach(int i in NUMQD) { solver.Add(field[ lt[i,0]-1, lt[i,1]-1 ] < field[ lt[i,2]-1, lt[i,3]-1 ] ); } // // Search // DecisionBuilder db = solver.MakePhase(field_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { foreach(int i in RANGE) { foreach(int j in RANGE) { Console.Write("{0} ", field[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Solving a simple crossword. * See http://www.hakank.org/or-tools/crossword2.py * * */ private static void Solve() { Solver solver = new Solver("Crossword"); // // data // String[] alpha = {"_","a","b","c","d","e","f", "g","h","i","j","k","l","m", "n","o","p","q","r","s","t", "u","v","w","x","y","z"}; int a=1; int b=2; int c=3; int d=4; int e=5; int f=6; int g=7; int h=8; int i=9; int j=10; int k=11; int l=12; int m=13; int n=14; int o=15; int p=16; int q=17; int r=18; int s=19; int t=20; int u=21; int v=22; int w=23; int x=24; int y=25; int z=26; const int num_words = 15; int word_len = 5; int[,] AA = {{h, o, s, e, s}, // HOSES {l, a, s, e, r}, // LASER {s, a, i, l, s}, // SAILS {s, h, e, e, t}, // SHEET {s, t, e, e, r}, // STEER {h, e, e, l, 0}, // HEEL {h, i, k, e, 0}, // HIKE {k, e, e, l, 0}, // KEEL {k, n, o, t, 0}, // KNOT {l, i, n, e, 0}, // LINE {a, f, t, 0, 0}, // AFT {a, l, e, 0, 0}, // ALE {e, e, l, 0, 0}, // EEL {l, e, e, 0, 0}, // LEE {t, i, e, 0, 0}}; // TIE int num_overlapping = 12; int[,] overlapping = {{0, 2, 1, 0}, // s {0, 4, 2, 0}, // s {3, 1, 1, 2}, // i {3, 2, 4, 0}, // k {3, 3, 2, 2}, // e {6, 0, 1, 3}, // l {6, 1, 4, 1}, // e {6, 2, 2, 3}, // e {7, 0, 5, 1}, // l {7, 2, 1, 4}, // s {7, 3, 4, 2}, // e {7, 4, 2, 4}}; // r int N = 8; // // Decision variables // // for labeling on A and E IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, 0, 26, "A"); IntVar[] A_flat = A.Flatten(); IntVar[] all = new IntVar[(num_words * word_len) + N]; for(int I = 0; I < num_words; I++) { for(int J = 0; J < word_len; J++) { all[I * word_len + J] = A[I,J]; } } IntVar[] E = solver.MakeIntVarArray(N, 0, num_words, "E"); for(int I = 0; I < N; I++) { all[num_words * word_len + I] = E[I]; } // // Constraints // solver.Add(E.AllDifferent()); for(int I = 0; I < num_words; I++) { for(int J = 0; J < word_len; J++) { solver.Add(A[I,J] == AA[I,J]); } } // This contraint handles the overlappings. // // It's coded in MiniZinc as // // forall(i in 1..num_overlapping) ( // A[E[overlapping[i,1]], overlapping[i,2]] = // A[E[overlapping[i,3]], overlapping[i,4]] // ) // and in or-tools/Python as // solver.Add( // solver.Element(A_flat,E[overlapping[I][0]]*word_len+overlapping[I][1]) // == // solver.Element(A_flat,E[overlapping[I][2]]*word_len+overlapping[I][3])) // for(int I = 0; I < num_overlapping; I++) { solver.Add( A_flat.Element(E[overlapping[I,0]] * word_len + overlapping[I,1]) == A_flat.Element(E[overlapping[I,2]] * word_len + overlapping[I,3])); } // // Search // DecisionBuilder db = solver.MakePhase(all, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { Console.WriteLine("E: "); for(int ee = 0; ee < N; ee++) { int e_val = (int)E[ee].Value(); Console.Write(ee + ": (" + e_val + ") "); for(int ii = 0; ii < word_len; ii++) { Console.Write(alpha[(int)A[ee,ii].Value()]); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Solves the Quasigroup Completion problem. * See http://www.hakank.org/or-tools/quasigroup_completion.py * */ private static void Solve() { Solver solver = new Solver("QuasigroupCompletion"); // // data // Console.WriteLine("Problem:"); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(problem[i,j] + " "); } Console.WriteLine(); } Console.WriteLine(); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (problem[i,j] > X) { solver.Add(x[i,j] == problem[i,j]); } } } // // rows and columns must be different // // rows for(int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; for(int j = 0; j < n; j++) { row[j] = x[i,j]; } solver.Add(row.AllDifferent()); } // columns for(int j = 0; j < n; j++) { IntVar[] col = new IntVar[n]; for(int i = 0; i < n; i++) { col[i] = x[i,j]; } solver.Add(col.AllDifferent()); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_SIMPLE, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); int sol = 0; while (solver.NextSolution()) { sol++; Console.WriteLine("Solution #{0} ", sol + " "); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++){ Console.Write("{0} ", x[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * From Programmers Stack Exchange (C#) * http://programmers.stackexchange.com/questions/153184/partitioning-set-into-subsets-with-respect-to-equality-of-sum-among-subsets * Partitioning set into subsets with respect to equality of sum among subsets * """ * let say i have {3, 1, 1, 2, 2, 1,5,2,7} set of numbers, I need to split the * numbers such that sum of subset1 should be equal to sum of subset2 * {3,2,7} {1,1,2,1,5,2}. First we should identify whether we can split number(one * way might be dividable by 2 without any remainder) and if we can, we should * write our algorithm two create s1 and s2 out of s. * * How to proceed with this approach? I read partition problem in wiki and even in some * articles but i am not able to get anything. Can someone help me to find the * right algorithm and its explanation in simple English? * * * Also see http://www.hakank.org/or-tools/set_partition.cs * * Model by Hakan Kjellerstrand ([email protected]) * See other or-tools/C# models at http://www.hakank.org/or-tools/#csharp * */ private static void Solve() { Solver solver = new Solver("PartitionSets"); // // Data // int[] s = {3, 1, 1, 2, 2, 1, 5, 2, 7}; int n = s.Length; int num_subsets = 2; IEnumerable<int> NRange = Enumerable.Range(0, n); IEnumerable<int> Sets = Enumerable.Range(0, num_subsets); // // Decision variables // // To which subset do x[i] belong? IntVar[,] x = solver.MakeIntVarMatrix(num_subsets, n, 0, 1, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // Ensure that a number is in exact one subset for(int k = 0; k < n; k++) { solver.Add( (from p in Sets select (x[p,k])).ToArray().Sum() == 1); } // Ensure that the sum of all subsets are the same. for(int p = 0; p < num_subsets-1; p++) { solver.Add( (from k in NRange select (s[k]*x[p,k])).ToArray().Sum() == (from k in NRange select (s[k]*x[p+1,k])).ToArray().Sum() ); } // symmetry breaking: assign first number to subset 1 solver.Add(x[0,0] == 1); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { foreach(int i in Sets) { Console.Write("subset " + i + ": "); foreach(int j in NRange) { if ((int)x[i,j].Value() == 1) { Console.Write(s[j] + " "); } } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Solves the Minesweeper problems. * * See http://www.hakank.org/google_or_tools/minesweeper.py * */ private static void Solve() { Solver solver = new Solver("Minesweeper"); // // data // int[] S = {-1, 0, 1}; Console.WriteLine("Problem:"); for(int i = 0; i < r; i++) { for(int j = 0; j < c; j++) { if (game[i,j] > X) { Console.Write(game[i,j] + " "); } else { Console.Write("X "); } } Console.WriteLine(); } Console.WriteLine(); // // Decision variables // IntVar[,] mines = solver.MakeIntVarMatrix(r, c, 0, 1, "mines"); // for branching IntVar[] mines_flat = mines.Flatten(); // // Constraints // for(int i = 0; i < r; i++) { for(int j = 0; j < c; j++) { if (game[i,j] >= 0) { solver.Add( mines[i,j] == 0); // this cell is the sum of all its neighbours var tmp = from a in S from b in S where i + a >= 0 && j + b >= 0 && i + a < r && j + b < c select(mines[i+a,j+b]); solver.Add(tmp.ToArray().Sum() == game[i,j]); } if (game[i,j] > X) { // This cell cannot be a mine since it // has some value assigned to it solver.Add(mines[i,j] == 0); } } } // // Search // DecisionBuilder db = solver.MakePhase(mines_flat, Solver.CHOOSE_PATH, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); int sol = 0; while (solver.NextSolution()) { sol++; Console.WriteLine("Solution #{0} ", sol + " "); for(int i = 0; i < r; i++) { for(int j = 0; j < c; j++){ Console.Write("{0} ", mines[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * P-median problem. * * Model and data from the OPL Manual, which describes the problem: * """ * The P-Median problem is a well known problem in Operations Research. * The problem can be stated very simply, like this: given a set of customers * with known amounts of demand, a set of candidate locations for warehouses, * and the distance between each pair of customer-warehouse, choose P * warehouses to open that minimize the demand-weighted distance of serving * all customers from those P warehouses. * """ * * Also see http://www.hakank.org/or-tools/p_median.py * */ private static void Solve() { Solver solver = new Solver("PMedian"); // // Data // int p = 2; int num_customers = 4; IEnumerable<int> CUSTOMERS = Enumerable.Range(0, num_customers); int num_warehouses = 3; IEnumerable<int> WAREHOUSES = Enumerable.Range(0, num_warehouses); int[] demand = {100,80,80,70}; int [,] distance = { { 2, 10, 50}, { 2, 10, 52}, {50, 60, 3}, {40, 60, 1} }; // // Decision variables // IntVar[] open = solver.MakeIntVarArray(num_warehouses, 0, num_warehouses, "open"); IntVar[,] ship = solver.MakeIntVarMatrix(num_customers, num_warehouses, 0, 1, "ship"); IntVar z = solver.MakeIntVar(0, 1000, "z"); // // Constraints // solver.Add((from c in CUSTOMERS from w in WAREHOUSES select (demand[c]*distance[c,w]*ship[c,w]) ).ToArray().Sum() == z); solver.Add(open.Sum() == p); foreach(int c in CUSTOMERS) { foreach(int w in WAREHOUSES) { solver.Add(ship[c,w] <= open[w]); } solver.Add((from w in WAREHOUSES select ship[c,w]).ToArray().Sum() == 1); } // // Objective // OptimizeVar obj = z.Minimize(1); // // Search // DecisionBuilder db = solver.MakePhase(open.Concat(ship.Flatten()).ToArray(), Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db, obj); while (solver.NextSolution()) { Console.WriteLine("z: {0}",z.Value()); Console.Write("open:"); foreach(int w in WAREHOUSES) { Console.Write(open[w].Value() + " "); } Console.WriteLine(); foreach(int c in CUSTOMERS) { foreach(int w in WAREHOUSES) { Console.Write(ship[c,w].Value()+ " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Magic squares and cards problem. * * Martin Gardner (July 1971) * """ * Allowing duplicates values, what is the largest constant sum for an order-3 * magic square that can be formed with nine cards from the deck. * """ * * * Also see http://www.hakank.org/or-tools/magic_square_and_cards.py * */ private static void Solve(int n=3) { Solver solver = new Solver("MagicSquareAndCards"); IEnumerable<int> RANGE = Enumerable.Range(0, n); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, 13, "x"); IntVar[] x_flat = x.Flatten(); IntVar s = solver.MakeIntVar(1, 13*4, "s"); IntVar[] counts = solver.MakeIntVarArray(14, 0, 4, "counts"); // // Constraints // solver.Add(x_flat.Distribute(counts)); // the standard magic square constraints (sans all_different) foreach(int i in RANGE) { // rows solver.Add( (from j in RANGE select x[i,j]).ToArray().Sum() == s); // columns solver.Add( (from j in RANGE select x[j,i]).ToArray().Sum() == s); } // diagonals solver.Add( (from i in RANGE select x[i,i]).ToArray().Sum() == s); solver.Add( (from i in RANGE select x[i,n-i-1]).ToArray().Sum() == s); // redundant constraint solver.Add(counts.Sum() == n*n); // // Objective // OptimizeVar obj = s.Maximize(1); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MAX_VALUE); solver.NewSearch(db, obj); while (solver.NextSolution()) { Console.WriteLine("s: {0}", s.Value()); Console.Write("counts:"); for(int i = 0; i < 14; i++) { Console.Write(counts[i].Value() + " "); } Console.WriteLine(); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(x[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * A programming puzzle from Einav. * * From * "A programming puzzle from Einav" * http://gcanyon.wordpress.com/2009/10/28/a-programming-puzzle-from-einav/ * """ * My friend Einav gave me this programming puzzle to work on. Given * this array of positive and negative numbers: * 33 30 -10 -6 18 7 -11 -23 6 * ... * -25 4 16 30 33 -23 -4 4 -23 * * You can flip the sign of entire rows and columns, as many of them * as you like. The goal is to make all the rows and columns sum to positive * numbers (or zero), and then to find the solution (there are more than one) * that has the smallest overall sum. So for example, for this array: * 33 30 -10 * -16 19 9 * -17 -12 -14 * You could flip the sign for the bottom row to get this array: * 33 30 -10 * -16 19 9 * 17 12 14 * Now all the rows and columns have positive sums, and the overall total is * 108. * But you could instead flip the second and third columns, and the second * row, to get this array: * 33 -30 10 * 16 19 9 * -17 12 14 * All the rows and columns still total positive, and the overall sum is just * 66. So this solution is better (I don't know if it's the best) * A pure brute force solution would have to try over 30 billion solutions. * I wrote code to solve this in J. I'll post that separately. * """ * * Note: * This is a port of Larent Perrons's Python version of my own einav_puzzle.py. * He removed some of the decision variables and made it more efficient. * Thanks! * * Also see http://www.hakank.org/or-tools/einav_puzzle2.py * */ private static void Solve() { Solver solver = new Solver("EinavPuzzle2"); // // Data // // Small problem // int rows = 3; // int cols = 3; // int[,] data = { // { 33, 30, -10}, // {-16, 19, 9}, // {-17, -12, -14} // }; // Full problem int rows = 27; int cols = 9; int[,] data = { {33,30,10,-6,18,-7,-11,23,-6}, {16,-19,9,-26,-8,-19,-8,-21,-14}, {17,12,-14,31,-30,13,-13,19,16}, {-6,-11,1,17,-12,-4,-7,14,-21}, {18,-31,34,-22,17,-19,20,24,6}, {33,-18,17,-15,31,-5,3,27,-3}, {-18,-20,-18,31,6,4,-2,-12,24}, {27,14,4,-29,-3,5,-29,8,-12}, {-15,-7,-23,23,-9,-8,6,8,-12}, {33,-23,-19,-4,-8,-7,11,-12,31}, {-20,19,-15,-30,11,32,7,14,-5}, {-23,18,-32,-2,-31,-7,8,24,16}, {32,-4,-10,-14,-6,-1,0,23,23}, {25,0,-23,22,12,28,-27,15,4}, {-30,-13,-16,-3,-3,-32,-3,27,-31}, {22,1,26,4,-2,-13,26,17,14}, {-9,-18,3,-20,-27,-32,-11,27,13}, {-17,33,-7,19,-32,13,-31,-2,-24}, {-31,27,-31,-29,15,2,29,-15,33}, {-18,-23,15,28,0,30,-4,12,-32}, {-3,34,27,-25,-18,26,1,34,26}, {-21,-31,-10,-13,-30,-17,-12,-26,31}, {23,-31,-19,21,-17,-10,2,-23,23}, {-3,6,0,-3,-32,0,-10,-25,14}, {-19,9,14,-27,20,15,-5,-27,18}, {11,-6,24,7,-17,26,20,-31,-25}, {-25,4,-16,30,33,23,-4,-4,23} }; IEnumerable<int> ROWS = Enumerable.Range(0, rows); IEnumerable<int> COLS = Enumerable.Range(0, cols); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(rows, cols, -100, 100, "x"); IntVar[] x_flat = x.Flatten(); int[] signs_domain = {-1,1}; // This don't work at the moment... IntVar[] row_signs = solver.MakeIntVarArray(rows, signs_domain, "row_signs"); IntVar[] col_signs = solver.MakeIntVarArray(cols, signs_domain, "col_signs"); // To optimize IntVar total_sum = x_flat.Sum().VarWithName("total_sum"); // // Constraints // foreach(int i in ROWS) { foreach(int j in COLS) { solver.Add(x[i,j] == data[i,j] * row_signs[i] * col_signs[j]); } } // row sums IntVar[] row_sums = (from i in ROWS select (from j in COLS select x[i,j] ).ToArray().Sum().Var()).ToArray(); foreach(int i in ROWS) { row_sums[i].SetMin(0); } // col sums IntVar[] col_sums = (from j in COLS select (from i in ROWS select x[i,j] ).ToArray().Sum().Var()).ToArray(); foreach(int j in COLS) { col_sums[j].SetMin(0); } // // Objective // OptimizeVar obj = total_sum.Minimize(1); // // Search // DecisionBuilder db = solver.MakePhase(col_signs.Concat(row_signs).ToArray(), Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, Solver.ASSIGN_MAX_VALUE); solver.NewSearch(db, obj); while (solver.NextSolution()) { Console.WriteLine("Sum: {0}",total_sum.Value()); Console.Write("row_sums: "); foreach(int i in ROWS) { Console.Write(row_sums[i].Value() + " "); } Console.Write("\nrow_signs: "); foreach(int i in ROWS) { Console.Write(row_signs[i].Value() + " "); } Console.Write("\ncol_sums: "); foreach(int j in COLS) { Console.Write(col_sums[j].Value() + " "); } Console.Write("\ncol_signs: "); foreach(int j in COLS) { Console.Write(col_signs[j].Value() + " "); } Console.WriteLine("\n"); foreach(int i in ROWS) { foreach(int j in COLS) { Console.Write("{0,3} ", x[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Kakuru puzzle. * * http://en.wikipedia.org/wiki/Kakuro * """ * The object of the puzzle is to insert a digit from 1 to 9 inclusive * into each white cell such that the sum of the numbers in each entry * matches the clue associated with it and that no digit is duplicated in * any entry. It is that lack of duplication that makes creating Kakuro * puzzles with unique solutions possible, and which means solving a Kakuro * puzzle involves investigating combinations more, compared to Sudoku in * which the focus is on permutations. There is an unwritten rule for * making Kakuro puzzles that each clue must have at least two numbers * that add up to it. This is because including one number is mathematically * trivial when solving Kakuro puzzles; one can simply disregard the * number entirely and subtract it from the clue it indicates. * """ * * This model solves the problem at the Wikipedia page. * For a larger picture, see * http://en.wikipedia.org/wiki/File:Kakuro_black_box.svg * * The solution: * 9 7 0 0 8 7 9 * 8 9 0 8 9 5 7 * 6 8 5 9 7 0 0 * 0 6 1 0 2 6 0 * 0 0 4 6 1 3 2 * 8 9 3 1 0 1 4 * 3 1 2 0 0 2 1 * * Also see http://www.hakank.org/or-tools/kakuro.py * though this C# model has another representation of * the problem instance. * */ private static void Solve() { Solver solver = new Solver("Kakuro"); // size of matrix int n = 7; // segments: // sum, the segments // Note: this is 1-based int[][] problem = { new int[] {16, 1,1, 1,2}, new int[] {24, 1,5, 1,6, 1,7}, new int[] {17, 2,1, 2,2}, new int[] {29, 2,4, 2,5, 2,6, 2,7}, new int[] {35, 3,1, 3,2, 3,3, 3,4, 3,5}, new int[] { 7, 4,2, 4,3}, new int[] { 8, 4,5, 4,6}, new int[] {16, 5,3, 5,4, 5,5, 5,6, 5,7}, new int[] {21, 6,1, 6,2, 6,3, 6,4}, new int[] { 5, 6,6, 6,7}, new int[] { 6, 7,1, 7,2, 7,3}, new int[] { 3, 7,6, 7,7}, new int[] {23, 1,1, 2,1, 3,1}, new int[] {30, 1,2, 2,2, 3,2, 4,2}, new int[] {27, 1,5, 2,5, 3,5, 4,5, 5,5}, new int[] {12, 1,6, 2,6}, new int[] {16, 1,7, 2,7}, new int[] {17, 2,4, 3,4}, new int[] {15, 3,3, 4,3, 5,3, 6,3, 7,3}, new int[] {12, 4,6, 5,6, 6,6, 7,6}, new int[] { 7, 5,4, 6,4}, new int[] { 7, 5,7, 6,7, 7,7}, new int[] {11, 6,1, 7,1}, new int[] {10, 6,2, 7,2} }; int num_p = 24; // Number of segments // The blanks // Note: 1-based int[,] blanks = { {1,3}, {1,4}, {2,3}, {3,6}, {3,7}, {4,1}, {4,4}, {4,7}, {5,1}, {5,2}, {6,5}, {7,4}, {7,5} }; int num_blanks = blanks.GetLength(0); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // fill the blanks with 0 for(int i = 0; i < num_blanks; i++) { solver.Add(x[blanks[i,0]-1,blanks[i,1]-1]==0); } for(int i = 0; i < num_p; i++) { int[] segment = problem[i]; // Remove the sum from the segment int[] s2 = new int[segment.Length-1]; for(int j = 1; j < segment.Length; j++) { s2[j-1] = segment[j]; } // sum this segment calc(solver, s2, x, segment[0]); // all numbers in this segment must be distinct int len = segment.Length / 2; solver.Add( (from j in Enumerable.Range(0, len) select x[s2[j * 2] - 1, s2[j * 2 + 1] - 1]) .ToArray().AllDifferent()); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { int v = (int)x[i,j].Value(); if (v > 0) { Console.Write(v + " "); } else { Console.Write(" "); } } Console.WriteLine(); } } 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(); }
/** * * KenKen puzzle. * * http://en.wikipedia.org/wiki/KenKen * """ * KenKen or KEN-KEN is a style of arithmetic and logical puzzle sharing * several characteristics with sudoku. The name comes from Japanese and * is translated as 'square wisdom' or 'cleverness squared'. * ... * The objective is to fill the grid in with the digits 1 through 6 such that: * * * Each row contains exactly one of each digit * * Each column contains exactly one of each digit * * Each bold-outlined group of cells is a cage containing digits which * achieve the specified result using the specified mathematical operation: * addition (+), * subtraction (-), * multiplication (x), * and division (/). * (Unlike in Killer sudoku, digits may repeat within a group.) * * ... * More complex KenKen problems are formed using the principles described * above but omitting the symbols +, -, x and /, thus leaving them as * yet another unknown to be determined. * """ * * The solution is: * * 5 6 3 4 1 2 * 6 1 4 5 2 3 * 4 5 2 3 6 1 * 3 4 1 2 5 6 * 2 3 6 1 4 5 * 1 2 5 6 3 4 * * * Also see http://www.hakank.org/or-tools/kenken2.py * though this C# model has another representation of * the problem instance. * */ private static void Solve() { Solver solver = new Solver("KenKen2"); // size of matrix int n = 6; IEnumerable<int> RANGE = Enumerable.Range(0, n); // For a better view of the problem, see // http://en.wikipedia.org/wiki/File:KenKenProblem.svg // hints // sum, the hints // Note: this is 1-based int[][] problem = { new int[] { 11, 1,1, 2,1}, new int[] { 2, 1,2, 1,3}, new int[] { 20, 1,4, 2,4}, new int[] { 6, 1,5, 1,6, 2,6, 3,6}, new int[] { 3, 2,2, 2,3}, new int[] { 3, 2,5, 3,5}, new int[] {240, 3,1, 3,2, 4,1, 4,2}, new int[] { 6, 3,3, 3,4}, new int[] { 6, 4,3, 5,3}, new int[] { 7, 4,4, 5,4, 5,5}, new int[] { 30, 4,5, 4,6}, new int[] { 6, 5,1, 5,2}, new int[] { 9, 5,6, 6,6}, new int[] { 8, 6,1, 6,2, 6,3}, new int[] { 2, 6,4, 6,5} }; int num_p = problem.GetLength(0); // Number of segments // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // // alldifferent rows and columns foreach(int i in RANGE) { // rows solver.Add( (from j in RANGE select x[i,j]).ToArray().AllDifferent()); // cols solver.Add( (from j in RANGE select x[j,i]).ToArray().AllDifferent()); } // Calculate the segments for(int i = 0; i < num_p; i++) { int[] segment = problem[i]; // Remove the sum from the segment int len = segment.Length-1; int[] s2 = new int[len]; Array.Copy(segment, 1, s2, 0, len); // sum this segment calc(solver, s2, x, segment[0]); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.INT_VAR_DEFAULT, Solver.INT_VALUE_DEFAULT); solver.NewSearch(db); while (solver.NextSolution()) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(x[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Solves the Seseman convent problem. * See http://www.hakank.org/google_or_tools/seseman.py * */ private static void Solve(int n = 3) { Solver solver = new Solver("Seseman"); // // data // int border_sum = n * n; // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, n*n, "x"); IntVar[] x_flat = x.Flatten(); IntVar total_sum = x_flat.Sum().Var(); // // Constraints // // zero in all middle cells for(int i = 1; i < n-1; i++) { for(int j = 1; j < n-1; j++) { solver.Add(x[i,j] == 0); } } // all borders must be >= 1 for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (i == 0 || j == 0 || i == n - 1 || j == n - 1) { solver.Add(x[i,j] >= 1); } } } // sum the four borders IntVar[] border1 = new IntVar[n]; IntVar[] border2 = new IntVar[n]; IntVar[] border3 = new IntVar[n]; IntVar[] border4 = new IntVar[n]; for(int i = 0; i < n; i++) { border1[i] = x[i,0]; border2[i] = x[i,n-1]; border3[i] = x[0,i]; border4[i] = x[n-1,i]; } solver.Add(border1.Sum() == border_sum); solver.Add(border2.Sum() == border_sum); solver.Add(border3.Sum() == border_sum); solver.Add(border4.Sum() == border_sum); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_PATH, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { Console.WriteLine("total_sum: {0} ", total_sum.Value()); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++){ Console.Write("{0} ", x[i,j].Value()); } Console.WriteLine(); } Console.WriteLine(); } 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(); }
/** * * Implements Young tableaux and partitions. * See http://www.hakank.org/or-tools/young_tableuax.py * */ private static void Solve(int n) { Solver solver = new Solver("YoungTableaux"); // // data // Console.WriteLine("n: {0}\n", n); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n + 1, "x"); IntVar[] x_flat = x.Flatten(); // partition structure IntVar[] p = solver.MakeIntVarArray(n, 0, n + 1, "p"); // // Constraints // // 1..n is used exactly once for(int i = 1; i <= n; i++) { solver.Add(x_flat.Count(i, 1)); } solver.Add(x[0,0] == 1); // row wise for(int i = 0; i < n; i++) { for(int j = 1; j < n; j++) { solver.Add(x[i,j] >= x[i,j - 1]); } } // column wise for(int j = 0; j < n; j++) { for(int i = 1; i < n; i++) { solver.Add(x[i,j] >= x[i - 1, j]); } } // calculate the structure (i.e. the partition) for(int i = 0; i < n; i++) { IntVar[] b = new IntVar[n]; for(int j = 0; j < n; j++) { b[j] = x[i, j] <= n; } solver.Add(p[i] == b.Sum()); } solver.Add(p.Sum() == n); for(int i = 1; i < n; i++) { solver.Add(p[i - 1] >= p[i]); } // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); solver.NewSearch(db); while (solver.NextSolution()) { Console.Write("p: "); for(int i = 0; i < n; i++) { Console.Write(p[i].Value() + " "); } Console.WriteLine("\nx:"); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { long val = x[i,j].Value(); if (val <= n) { Console.Write(val + " "); } } if (p[i].Value() > 0) { Console.WriteLine(); } } Console.WriteLine(); } 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(); }
/** * * Solves the Coins Grid problm. * See http://www.hakank.org/google_or_tools/coins_grid.py * */ private static void Solve(int n = 31, int c = 14) { Solver solver = new Solver("CoinsGrid"); // // Decision variables // IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 1 , "x"); IntVar[] x_flat = x.Flatten(); // // Constraints // // sum row/columns == c for(int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; IntVar[] col = new IntVar[n]; for(int j = 0; j < n; j++) { row[j] = x[i,j]; col[j] = x[j,i]; } solver.Add(row.Sum() == c); solver.Add(col.Sum() == c); } // quadratic horizonal distance IntVar[] obj_tmp = new IntVar[n * n]; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { obj_tmp[i * n + j] = (x[i,j] * (i - j) * (i - j)).Var(); } } IntVar obj_var = obj_tmp.Sum().Var(); // // Objective // OptimizeVar obj = obj_var.Minimize(1); // // Search // DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MAX_VALUE); solver.NewSearch(db, obj); while (solver.NextSolution()) { Console.WriteLine("obj: " + obj_var.Value()); for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { Console.Write(x[i,j].Value() + " "); } Console.WriteLine(); } Console.WriteLine(); } 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(); }