예제 #1
0
파일: Online.cs 프로젝트: southpolenator/CG
    public static void Solve(Box[] boxes)
    {
        Stopwatch sw = Stopwatch.StartNew();

        Truck[] trucks = new Truck[100];
        for (int i = 0; i < trucks.Length; i++)
        {
            trucks[i] = new Truck()
            {
                Id = i,
            }
        }
        ;

        double       minTargetWeight = boxes.Sum(b => (double)b.Weight) / trucks.Length;
        TargetWeight targetWeight    = new TargetWeight()
        {
            Min = (int)Math.Floor(minTargetWeight),
            Max = (int)Math.Ceiling(minTargetWeight),
        };

        foreach (Box b in boxes.OrderBy(b => - b.Weight))
        {
            Truck truck        = null;
            int   truckBalance = int.MaxValue;

            foreach (Truck t in trucks)
            {
                int balance = t.Weight;

                if (balance < truckBalance)
                {
                    truck        = t;
                    truckBalance = balance;
                }
            }
            truck.AddBox(b);
        }

        int bestScore = int.MaxValue;

        Truck[] solution = null;
        int     ComplexOptimizationMaxMoves = boxes.Length < 1000 ? 1500 : 250;

#if LOCAL
        double lastPrint = 0;
#endif

        while (sw.Elapsed.TotalSeconds < MaxExecutionTimeSeconds && bestScore > targetWeight.Max - targetWeight.Min)
        {
            // Fix volume problems
            foreach (Truck truck1 in trucks.OrderBy(t => - t.Volume).ToArray())
            {
                while (truck1.Volume > Truck.MaxVolume)
                {
                    Box   box1        = null;
                    Box   box2        = null;
                    Truck truck2      = null;
                    int   bestBalance = int.MaxValue;

                    foreach (Truck t2 in trucks)
                    {
                        if (truck1 != t2 && t2.Volume < Truck.MaxVolume)
                        {
                            foreach (Box b1 in truck1.Boxes)
                            {
                                foreach (Box b2 in t2.Boxes)
                                {
                                    if (b1.Volume > b2.Volume && t2.Volume + b1.Volume - b2.Volume < Truck.MaxVolume)
                                    {
                                        int t1weight = truck1.Weight - b1.Weight + b2.Weight;
                                        int t2weight = t2.Weight - b2.Weight + b1.Weight;
                                        int balance  = (targetWeight.Distance(t1weight) + targetWeight.Distance(t2weight)) - (targetWeight.Distance(truck1.Weight) + targetWeight.Distance(t2.Weight));

                                        if (balance < bestBalance)
                                        {
                                            box1        = b1;
                                            box2        = b2;
                                            truck2      = t2;
                                            bestBalance = balance;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (box1 == null)
                    {
                        break;
                    }
                    truck1.RemoveBox(box1);
                    truck2.RemoveBox(box2);
                    truck1.AddBox(box2);
                    truck2.AddBox(box1);
                }
            }

            // Complex optimization
            ComplexOptimizationMaxMoves *= 2;
            foreach (Truck t in trucks)
            {
                t.UpdateMoves(ComplexOptimizationMaxMoves);
            }
            for (int skip = 0; skip < trucks.Length && sw.Elapsed.TotalSeconds < MaxExecutionTimeSeconds;)
            {
                int score = trucks.Max(t => t.Weight) - trucks.Min(t => t.Weight);

                if (score < bestScore && trucks.Max(t => t.Volume) < Truck.MaxVolume)
                {
                    bestScore = score;
                    solution  = boxes.Select(b => b.Truck).ToArray();
                }
#if LOCAL
                if (sw.Elapsed.TotalSeconds - lastPrint > 0.02)
                {
                    Console.Write($"{bestScore} {sw.Elapsed.TotalSeconds}s {score}                               \r");
                    lastPrint = sw.Elapsed.TotalSeconds;
                }
#endif
                Truck[] fats   = trucks.OrderBy(t => - targetWeight.Distance(t.Weight)).ThenBy(t => t.Id).Skip(skip).ToArray();
                Truck   truck1 = fats[0];

                if (targetWeight.Distance(truck1.Weight) == 0)
                {
                    break;
                }

                int         t1balance        = targetWeight.Distance(truck1.Weight);
                ComplexMove move1            = null;
                ComplexMove move2            = null;
                Truck       truck2           = null;
                int         newWeightBalance = t1balance;

                foreach (Truck t2 in fats)
                {
                    if (truck1 != t2)
                    {
                        int t2balance             = targetWeight.Distance(t2.Weight);
                        int previousWeightBalance = t1balance + t2balance;

                        if (sw.Elapsed.TotalSeconds >= MaxExecutionTimeSeconds)
                        {
                            break;
                        }

                        foreach (ComplexMove m1 in truck1.Moves)
                        {
                            int minM2Volume = (t2.Volume + m1.Volume) - Truck.MaxVolume;
                            int maxM2Volume = Truck.MaxVolume - (truck1.Volume - m1.Volume);
                            int start       = minM2Volume < t2.Moves[0].Volume ? 0 : LowerBound(t2.Moves, minM2Volume);
                            int end         = maxM2Volume > t2.Moves[t2.Moves.Length - 1].Volume ? t2.Moves.Length : UpperBound(t2.Moves, maxM2Volume);
                            int t1weight    = truck1.Weight - m1.Weight;
                            int t2weight    = t2.Weight + m1.Weight;

                            for (int i = start; i < end; i++)
                            {
                                ComplexMove m2 = t2.Moves[i];

                                int t1w = t1weight + m2.Weight;

                                int weightBalance = targetWeight.Distance(t1w);

                                if (weightBalance >= newWeightBalance)
                                {
                                    continue;
                                }

                                int t2w     = t2weight - m2.Weight;
                                int balance = targetWeight.Distance(t2w) - t1balance;

                                if (balance >= 0)
                                {
                                    continue;
                                }

                                {
                                    move1            = m1;
                                    move2            = m2;
                                    truck2           = t2;
                                    newWeightBalance = weightBalance;
                                }
                            }
                        }
                    }
                }
                if (truck2 == null)
                {
                    skip++;
                    continue;
                }

                skip = 0;
                foreach (Box b in move1.Boxes)
                {
                    truck1.RemoveBox(b);
                }
                foreach (Box b in move2.Boxes)
                {
                    truck2.RemoveBox(b);
                }
                foreach (Box b in move2.Boxes)
                {
                    truck1.AddBox(b);
                }
                foreach (Box b in move1.Boxes)
                {
                    truck2.AddBox(b);
                }
                truck1.UpdateMoves(ComplexOptimizationMaxMoves);
                truck2.UpdateMoves(ComplexOptimizationMaxMoves);
            }

#if LOCAL
            //foreach (Truck t in trucks.OrderBy(t => targetWeight.Distance(t.Weight)).ToArray())
            //    Console.WriteLine($"{t.Id}: {targetWeight.Distance(t.Weight)}[{t.Boxes.Count}]   {t.Weight / (double)Box.FixedPoint}  {t.Volume / (double)Box.FixedPoint}");
#endif

            // Randomize a bit
            Truck[] ttt     = trucks.Where(t => targetWeight.Distance(t.Weight) > 0).OrderBy(t => targetWeight.Distance(t.Weight)).ToArray();
            bool[]  touched = new bool[ttt.Length];

            for (int i = 0; i < ttt.Length / 2 && sw.Elapsed.TotalSeconds < MaxExecutionTimeSeconds; i++)
            {
                if (!touched[i])
                {
                    Truck       t1           = ttt[i];
                    Truck       truck1       = t1;
                    Truck       truck2       = null;
                    int         bestj        = -1;
                    ComplexMove move1        = null;
                    ComplexMove move2        = null;
                    int         bestDistance = int.MaxValue;
                    for (int j = 0; j < ttt.Length; j++)
                    {
                        if (i != j)
                        {
                            Truck t2 = ttt[j];

                            foreach (ComplexMove m1 in t1.Moves)
                            {
                                if (m1.Boxes.Length != t1.Boxes.Count)
                                {
                                    int minM2Volume = (t2.Volume + m1.Volume) - Truck.MaxVolume;
                                    int maxM2Volume = Truck.MaxVolume - (truck1.Volume - m1.Volume);
                                    int start       = minM2Volume < t2.Moves[0].Volume ? 0 : LowerBound(t2.Moves, minM2Volume);
                                    int end         = maxM2Volume > t2.Moves[t2.Moves.Length - 1].Volume ? t2.Moves.Length : UpperBound(t2.Moves, maxM2Volume);

                                    for (int m2i = start; m2i < end; m2i++)
                                    {
                                        ComplexMove m2       = t2.Moves[m2i];
                                        int         distance = Math.Abs(m1.Weight - m2.Weight);

                                        if (distance < bestDistance ||
                                            (distance == bestDistance && m1.Boxes.Length + m2.Boxes.Length > move1.Boxes.Length + move2.Boxes.Length))
                                        {
                                            move1        = m1;
                                            move2        = m2;
                                            truck2       = t2;
                                            bestDistance = distance;
                                            bestj        = j;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (bestj == -1)
                    {
                        continue;
                    }
                    touched[i]     = true;
                    touched[bestj] = true;
                    foreach (Box b in move1.Boxes)
                    {
                        truck1.RemoveBox(b);
                    }
                    foreach (Box b in move2.Boxes)
                    {
                        truck2.RemoveBox(b);
                    }
                    foreach (Box b in move2.Boxes)
                    {
                        truck1.AddBox(b);
                    }
                    foreach (Box b in move1.Boxes)
                    {
                        truck2.AddBox(b);
                    }
                    truck1.UpdateMoves(ComplexOptimizationMaxMoves);
                    truck2.UpdateMoves(ComplexOptimizationMaxMoves);
                }
            }
        }

        for (int i = 0; i < solution.Length; i++)
        {
            boxes[i].Truck = solution[i];
        }
#if !LOCAL
        Console.Error.WriteLine($"Time: {sw.Elapsed.TotalSeconds}s");
#endif
    }