Explorar o código

Part 1 : 75 ms
Part 2 : 320 ms

bastien.monsarrat %!s(int64=6) %!d(string=hai) anos
pai
achega
5b5998a131
Modificáronse 1 ficheiros con 58 adicións e 36 borrados
  1. 58 36
      D11.1/Program.cs

+ 58 - 36
D11.1/Program.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
-using System.Linq;
 
 namespace D11._1
 {
@@ -71,27 +70,26 @@ namespace D11._1
                 return generators >= 0 && unpluggedMicroships == 0;
             }
 
-            static readonly int nelement = Enum.GetValues(typeof(EnumElement)).Length;
+            static readonly int NElements = Enum.GetValues(typeof(EnumElement)).Length;
             public Floor()
             {
                 for (int i = 0; i < Objects.Length; ++i)
-                    Objects[i] = new bool[nelement];
+                    Objects[i] = new bool[NElements];
             }
 
             public object Clone()
             {
                 var floor = new Floor();
+
                 for (var i = 0; i < Objects.Length; i++)
-                    for (var j = 0; j < Objects[i].Length; ++j)
-                        floor.Objects[i][j] = Objects[i][j];
+                    Objects[i].CopyTo(floor.Objects[i], 0);
+
                 return floor;
             }
 
             public bool Set(EnumType type, EnumElement element, bool value) => Objects[(int)type][(int)element] = value;
             public bool Add(EnumType type, EnumElement element) => Set(type, element, true);
 
-            public override bool Equals(object obj) => obj is Floor floor && floor.GetHashCode() == GetHashCode();
-
             public override int GetHashCode()
             {
                 //int h1 = 0, h2 = 0;
@@ -104,6 +102,18 @@ namespace D11._1
 
                 //return h1 * 100 + h2;
 
+                /*
+                 * Firts hash method takes into account wich microship and generator element is
+                 * present on the floor.
+                 * 
+                 * The following methods just relies on the fact that it does not matter
+                 * which generators or microships are on the floor but that there are
+                 * paired microship/generators (it does not matter wich ones) and unpaired
+                 * microships and generators (still, it does not matter which ones)
+                 *
+                 * * ==> ENORMOUS performance gain : ~8s => 150ms
+                 */
+
                 int paired = 0, unpairedGen = 0, unpairedMicroship = 0;
 
                 for (var i = 0; i < Objects[0].Length; ++i)
@@ -121,8 +131,6 @@ namespace D11._1
             }
         }
 
-        public override bool Equals(object obj) => obj is Facility facility && facility.GetHashCode() == GetHashCode() && facility.Elevator == Elevator;
-
         public override int GetHashCode()
         {
             int hash = 0;
@@ -138,22 +146,20 @@ namespace D11._1
                 Floors = new Floor[Floors.Length]
             };
 
-            for (var i = 0; i < Floors.Length; ++i) nf.Floors[i] = (Floor) Floors[i].Clone();
+            for (var i = 0; i < Floors.Length; ++i) nf.Floors[i] = (Floor)Floors[i].Clone();
             return nf;
         }
     }
 
     class Program
     {
-        static readonly EnumElement[] aelement = Enum.GetValues(typeof(EnumElement)) as EnumElement[];
+        static readonly EnumElement[] AllElements = Enum.GetValues(typeof(EnumElement)) as EnumElement[];
+        static readonly int[] ElevatorDirections = new[] { -1, +1 };
 
-        static void RunFacilityTest(Facility startFacility)
+        static int RunFacilityTest(Facility startFacility)
         {
-            var upd = new [] { -1, +1 };
-            var cmp = Comparer<(Facility, int)>.Create((a, b) => a.Item2.CompareTo(b.Item2));
-
             var queue = new Queue<(Facility facility, int step)>();
-            var tested = new HashSet<Facility>();
+            var tested = new HashSet<int>();
 
             queue.Enqueue((startFacility, 0));
 
@@ -161,40 +167,54 @@ namespace D11._1
             {
                 (Facility facility, int step) = queue.Dequeue();
 
-                if (facility.IsOk())
-                {
-                    Console.WriteLine($"The answer is : {step}");
-                    break;
-                }
+                if (facility.IsOk())  return step;
 
-                foreach (((EnumType t, EnumElement e) c1, (EnumType t, EnumElement e)? c2) in GetCandidates(facility))
+                foreach (((EnumType, EnumElement) first, (EnumType, EnumElement)? second) in GetCandidates(facility))
                 {
-                    foreach (var direction in upd)
+                    foreach (var direction in ElevatorDirections)
                     {
                         if (facility.Elevator + direction < 0 || facility.Elevator + direction >= facility.Floors.Length) continue;
 
-                        Facility nfacility = BuildNewFacility(facility, c1, c2, direction);
-                        if (nfacility.CurrentFloor.IsValid() == false) continue;
+                        Facility nfacility = BuildNewFacility(facility, first, second, direction);
+                        if (nfacility == null) continue;
 
-                        if (tested.Contains(nfacility)) continue;
-                        tested.Add(nfacility);
+                        int hash = nfacility.GetHashCode() ^ nfacility.Elevator;
+
+                        if (tested.Contains(hash)) continue;
+                        tested.Add(hash);
 
                         queue.Enqueue((nfacility, step + 1));
                     }
                 }
             }
+
+            throw new Exception();
         }
 
-        private static Facility BuildNewFacility(Facility facility, (EnumType t, EnumElement e) c1, (EnumType t, EnumElement e)? c2, int d)
+        private static Facility BuildNewFacility(Facility facility, (EnumType t, EnumElement e) first, (EnumType t, EnumElement e)? second, int direction)
         {
+            /* Slight optimisation 
+             * Before cloning the entire facility, ensures that the next state for the current floor
+             * is valid as cloning a single floor is less consuming.
+             */
+            var clone = facility.Floors[facility.Elevator + direction].Clone() as Facility.Floor;
+
+            clone.Set(first.t, first.e, true);
+            if (second.HasValue) clone.Set(second.Value.t, second.Value.e, true);
+
+            if (clone.IsValid() == false) return null;
+
+            // -----------
+
             var nfacility = facility.Clone() as Facility;
-            nfacility.CurrentFloor.Objects[(int)c1.t][(int)c1.e] = false;
-            if (c2.HasValue) nfacility.CurrentFloor.Objects[(int)c2?.t][(int)c2?.e] = false;
 
-            nfacility.Elevator += d;
+            nfacility.CurrentFloor.Set(first.t, first.e, false);
+            if (second.HasValue) nfacility.CurrentFloor.Set(second.Value.t, second.Value.e, false);
+
+            nfacility.Elevator += direction;
+
+            nfacility.Floors[nfacility.Elevator] = clone;
 
-            nfacility.CurrentFloor.Objects[(int)c1.t][(int)c1.e] = true;
-            if (c2.HasValue) nfacility.CurrentFloor.Objects[(int)c2?.t][(int)c2?.e] = true;
             return nfacility;
         }
 
@@ -202,7 +222,7 @@ namespace D11._1
         {
             var all = new List<(EnumType, EnumElement)>();
 
-            foreach (EnumElement element in aelement)
+            foreach (EnumElement element in AllElements)
             {
                 if (facility.CurrentFloor.Objects[(int)EnumType.Generator][(int)element]) all.Add((EnumType.Generator, element));
                 if (facility.CurrentFloor.Objects[(int)EnumType.Microship][(int)element]) all.Add((EnumType.Microship, element));
@@ -248,7 +268,8 @@ namespace D11._1
 
             var sw = Stopwatch.StartNew();
 
-            RunFacilityTest(facility);
+            var answer = RunFacilityTest(facility);
+            Console.WriteLine($"The answer is : {answer}");
 
             sw.Stop();
 
@@ -284,7 +305,8 @@ namespace D11._1
 
             var sw = Stopwatch.StartNew();
 
-            RunFacilityTest(facility);
+            var answer = RunFacilityTest(facility);
+            Console.WriteLine($"The answer is : {answer}");
 
             sw.Stop();