static void Main()
    {
        CpModel model = new CpModel();
        // Three weeks.
        int horizon   = 100;
        int num_tasks = 4;

        IntVar[]      starts    = new IntVar[num_tasks];
        IntVar[]      ends      = new IntVar[num_tasks];
        IntervalVar[] intervals = new IntervalVar[num_tasks];
        ILiteral[]    presences = new ILiteral[num_tasks];
        IntVar[]      ranks     = new IntVar[num_tasks];

        IntVar true_var = model.NewConstant(1);

        // Creates intervals, half of them are optional.
        for (int t = 0; t < num_tasks; ++t)
        {
            starts[t] = model.NewIntVar(0, horizon, String.Format("start_{0}", t));
            int duration = t + 1;
            ends[t] = model.NewIntVar(0, horizon, String.Format("end_{0}", t));
            if (t < num_tasks / 2)
            {
                intervals[t] = model.NewIntervalVar(starts[t], duration, ends[t],
                                                    String.Format("interval_{0}", t));
                presences[t] = true_var;
            }
            else
            {
                presences[t] = model.NewBoolVar(String.Format("presence_{0}", t));
                intervals[t] = model.NewOptionalIntervalVar(
                    starts[t], duration, ends[t], presences[t],
                    String.Format("o_interval_{0}", t));
            }

            // Ranks = -1 if and only if the tasks is not performed.
            ranks[t] =
                model.NewIntVar(-1, num_tasks - 1, String.Format("rank_{0}", t));
        }

        // Adds NoOverlap constraint.
        model.AddNoOverlap(intervals);

        // Adds ranking constraint.
        RankTasks(model, starts, presences, ranks);

        // Adds a constraint on ranks.
        model.Add(ranks[0] < ranks[1]);

        // Creates makespan variable.
        IntVar makespan = model.NewIntVar(0, horizon, "makespan");

        for (int t = 0; t < num_tasks; ++t)
        {
            model.Add(ends[t] <= makespan).OnlyEnforceIf(presences[t]);
        }
        // Minimizes makespan - fixed gain per tasks performed.
        // As the fixed cost is less that the duration of the last interval,
        // the solver will not perform the last interval.
        IntVar[] presences_as_int_vars = new IntVar[num_tasks];
        for (int t = 0; t < num_tasks; ++t)
        {
            presences_as_int_vars[t] = (IntVar)presences[t];
        }
        model.Minimize(2 * makespan - 7 * presences_as_int_vars.Sum());

        // Creates a solver and solves the model.
        CpSolver       solver = new CpSolver();
        CpSolverStatus status = solver.Solve(model);

        if (status == CpSolverStatus.Optimal)
        {
            Console.WriteLine(String.Format("Optimal cost: {0}",
                                            solver.ObjectiveValue));
            Console.WriteLine(String.Format("Makespan: {0}", solver.Value(makespan)));
            for (int t = 0; t < num_tasks; ++t)
            {
                if (solver.BooleanValue(presences[t]))
                {
                    Console.WriteLine(String.Format(
                                          "Task {0} starts at {1} with rank {2}",
                                          t, solver.Value(starts[t]), solver.Value(ranks[t])));
                }
                else
                {
                    Console.WriteLine(String.Format(
                                          "Task {0} in not performed and ranked at {1}", t,
                                          solver.Value(ranks[t])));
                }
            }
        }
        else
        {
            Console.WriteLine(
                String.Format("Solver exited with nonoptimal status: {0}", status));
        }
    }
Пример #2
0
    public static void Main(String[] args)
    {
        // Instantiate the data problem.
        // [START data]
        int[] Weights  = { 48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36 };
        int[] Values   = { 10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25 };
        int   NumItems = Weights.Length;

        int[] allItems = Enumerable.Range(0, NumItems).ToArray();

        int[] BinCapacities = { 100, 100, 100, 100, 100 };
        int   NumBins       = BinCapacities.Length;

        int[] allBins = Enumerable.Range(0, NumBins).ToArray();
        // [END data]

        // Model.
        // [START model]
        CpModel model = new CpModel();

        // [END model]

        // Variables.
        // [START variables]
        ILiteral[,] x = new ILiteral[NumItems, NumBins];
        foreach (int i in allItems)
        {
            foreach (int b in allBins)
            {
                x[i, b] = model.NewBoolVar($"x_{i}_{b}");
            }
        }
        // [END variables]

        // Constraints.
        // [START constraints]
        // Each item is assigned to at most one bin.
        foreach (int i in allItems)
        {
            List <ILiteral> literals = new List <ILiteral>();
            foreach (int b in allBins)
            {
                literals.Add(x[i, b]);
            }
            model.AddAtMostOne(literals);
        }

        // The amount packed in each bin cannot exceed its capacity.
        foreach (int b in allBins)
        {
            List <ILiteral> items = new List <ILiteral>();
            foreach (int i in allItems)
            {
                items.Add(x[i, b]);
            }
            model.Add(LinearExpr.WeightedSum(items, Weights) <= BinCapacities[b]);
        }
        // [END constraints]

        // Objective.
        // [START objective]
        LinearExprBuilder obj = LinearExpr.NewBuilder();

        foreach (int i in allItems)
        {
            foreach (int b in allBins)
            {
                obj.AddTerm(x[i, b], Values[i]);
            }
        }
        model.Maximize(obj);
        //  [END objective]

        // Solve
        // [START solve]
        CpSolver       solver = new CpSolver();
        CpSolverStatus status = solver.Solve(model);

        // [END solve]

        // Print solution.
        // [START print_solution]
        // Check that the problem has a feasible solution.
        if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible)
        {
            Console.WriteLine($"Total packed value: {solver.ObjectiveValue}");
            double TotalWeight = 0.0;
            foreach (int b in allBins)
            {
                double BinWeight = 0.0;
                double BinValue  = 0.0;
                Console.WriteLine($"Bin {b}");
                foreach (int i in allItems)
                {
                    if (solver.BooleanValue(x[i, b]))
                    {
                        Console.WriteLine($"Item {i} weight: {Weights[i]} values: {Values[i]}");
                        BinWeight += Weights[i];
                        BinValue  += Values[i];
                    }
                }
                Console.WriteLine("Packed bin weight: " + BinWeight);
                Console.WriteLine("Packed bin value: " + BinValue);
                TotalWeight += BinWeight;
            }
            Console.WriteLine("Total packed weight: " + TotalWeight);
        }
        else
        {
            Console.WriteLine("No solution found.");
        }
        // [END print_solution]

        // [START statistics]
        Console.WriteLine("Statistics");
        Console.WriteLine($"  conflicts: {solver.NumConflicts()}");
        Console.WriteLine($"  branches : {solver.NumBranches()}");
        Console.WriteLine($"  wall time: {solver.WallTime()}s");
        // [END statistics]
    }
    static void Solve(int first_slot)
    {
        Console.WriteLine("----------------------------------------------------");

        // the slots each speaker is available
        int[][] speaker_availability =
        {
            new int[] {  1,  3,  4,  6,  7, 10, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59 },
            new int[] {  1,  2,  7,  8, 10, 11, 16, 17, 18, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 54, 55, 56, 57, 58, 59,60 },
            new int[] {  5,  6,  7, 10, 12, 14, 16, 17, 21, 22, 23, 24, 33, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 51, 53, 55, 56, 57, 58,59 },
            new int[] {  1,  2,  3,  4,  5,  6,  7, 11, 13, 14, 15, 16, 20, 24, 25, 33, 34, 35, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59,60 },
            new int[] {  4,  7,  8,  9, 16, 17, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 49, 50, 51, 53, 55, 56, 57, 58, 59,60 },
            new int[] {  4,  7,  9, 11, 12, 13, 14, 15, 16, 17, 18, 22, 23, 24, 33, 34, 35, 36, 38, 39, 42, 44, 46, 48, 49, 51, 53, 54, 55, 56,57 },
            new int[] {  1,  2,  3,  4,  5,  6,  7, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58, 59,60 },
            new int[] {  1,  3, 11, 14, 15, 16, 17, 21, 22, 23, 24, 25, 33, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 59,60 },
            new int[] {  1,  2,  3,  4,  5,  7,  8,  9, 10, 11, 13, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54, 55, 56, 57, 59,60 },
            new int[] { 24, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,60 },
            new int[] {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 52, 53,55, 56, 57, 58, 59, 60 },
            new int[] {  3,  4,  5,  6, 13, 15, 16, 17, 18, 19, 21, 22, 24, 25, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 47, 52, 53, 55, 57, 58, 59,60 },
            new int[] {  4,  5,  6,  8, 11, 12, 13, 14, 17, 19, 20, 22, 23, 24, 25, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 47, 48, 49, 50, 51, 52, 55, 56,57 },
            new int[] {  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,52, 53, 54, 55, 56, 57, 58, 59, 60 },
            new int[] { 12, 25, 33, 35, 36, 37, 39, 41, 42, 43, 48, 51, 52, 53, 54, 57, 59,60 },
            new int[] {  4,  8, 16, 17, 19, 23, 25, 33, 34, 35, 37, 41, 44, 45, 47, 48, 49,50 },
            new int[] {  3, 23, 24, 25, 33, 35, 36, 37, 38, 39, 40, 42, 43, 44, 49, 50, 53, 54, 55, 56, 57, 58,60 },
            new int[] {  7, 13, 19, 20, 22, 23, 24, 25, 33, 34, 35, 38, 40, 41, 42, 44, 45, 46, 47, 48, 49, 52, 53, 54, 58, 59, 60 }
        };
        // how long each talk lasts for each speaker
        int[] durations          = { 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
        int   sum_of_durations   = durations.Sum();
        int   number_of_speakers = durations.Length;

        // calculate the total number of slots (maximum in the availability array)
        // (and add the max durations)
        int last_slot =
            (from s in Enumerable.Range(0, number_of_speakers)
             select speaker_availability[s].Max()).Max();

        Console.WriteLine(
            $"Scheduling {number_of_speakers} speakers, for a total of " +
            $"{sum_of_durations} slots, during [{first_slot}..{last_slot}]");

        CpModel model = new CpModel();

        // We store the possible entries (var, start) for all talks filtered
        // from the duration and the speaker availability.
        List <Entry>[] entries = new List <Entry> [number_of_speakers];
        for (int speaker = 0; speaker < number_of_speakers; ++speaker)
        {
            entries[speaker] = new List <Entry>();
        }

        List <IntVar>[] contributions_per_slot = new List <IntVar> [last_slot + 1];
        for (int slot = 1; slot <= last_slot; ++slot)
        {
            contributions_per_slot[slot] = new List <IntVar>();
        }

        for (int speaker = 0; speaker < number_of_speakers; ++speaker)
        {
            List <IntVar> all_vars = new List <IntVar>();
            int           duration = durations[speaker];
            // Let's filter the possible starts.
            int availability = speaker_availability[speaker].Length;
            for (int index = 0; index < availability; ++index)
            {
                bool ok   = true;
                int  slot = speaker_availability[speaker][index];
                if (slot < first_slot)
                {
                    continue;
                }
                for (int offset = 1; offset < duration; ++offset)
                {
                    if (index + offset >= availability ||
                        speaker_availability[speaker][index + offset] != slot + offset)
                    {
                        // discontinuity.
                        ok = false;
                        break;
                    }
                }
                if (ok)
                {
                    IntVar var = model.NewBoolVar("speaker " + (speaker + 1) + " starts at " + slot);
                    entries[speaker].Add(new Entry(var, slot));
                    all_vars.Add(var);
                    for (int offset = 0; offset < duration; ++offset)
                    {
                        contributions_per_slot[slot + offset].Add(var);
                    }
                }
            }
            model.Add(all_vars.ToArray().Sum() == 1);
        }
        // Force the schedule to be consistent.
        for (int slot = first_slot; slot <= last_slot; ++slot)
        {
            model.Add(contributions_per_slot[slot].ToArray().Sum() <= 1);
        }

        // Creates last_slot.
        IntVar last_slot_var = model.NewIntVar(
            first_slot + sum_of_durations - 1, last_slot, "last_slot");

        for (int speaker = 0; speaker < number_of_speakers; speaker++)
        {
            int duration = durations[speaker];
            foreach (Entry e in entries[speaker])
            {
                model.Add(last_slot_var >= e.start + duration - 1).OnlyEnforceIf(e.var);
            }
        }

        model.Minimize(last_slot_var);

        // Creates the solver and solve.
        CpSolver solver = new CpSolver();

        solver.StringParameters = "num_search_workers:8";
        CpSolverStatus status = solver.Solve(model);

        if (status == CpSolverStatus.Optimal)
        {
            Console.WriteLine("\nLast used slot: " + solver.Value(last_slot_var));
            Console.WriteLine("Speakers (start..end):");
            for (int speaker = 0; speaker < number_of_speakers; speaker++)
            {
                foreach (Entry e in entries[speaker])
                {
                    if (solver.BooleanValue(e.var))
                    {
                        Console.WriteLine(
                            "  - speaker {0,2}: {1,2}..{2,2}", (speaker + 1),
                            e.start, (e.start + durations[speaker] - 1));
                    }
                }
            }
        }

        // Statistics.
        Console.WriteLine(solver.ResponseStats());
    }