Dramatic speedup for day 12 part 2

This enables a flood-fill of distances from the goal location to every other location on the grid. With that, we only have to locate the node with the lowest distance value.

Technically this also solves part 1, but I wanted to leave the forward search in place as a test of sorts that the algorithm works both ways.

Now if I can just get the parse sped up...
This commit is contained in:
2022-12-12 20:27:07 -06:00
parent 2f73611a7c
commit 61227ba688

View File

@ -35,9 +35,7 @@ internal class Day12 : Day
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
return neighbors.Equals(other.neighbors) return location.Equals(other.location);
&& distance == other.distance
&& location.Equals(other.location);
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@ -67,7 +65,7 @@ internal class Day12 : Day
location = loc; location = loc;
} }
public override int GetHashCode() => HashCode.Combine(neighbors, location); public override int GetHashCode() => location.GetHashCode();
public override string ToString() => $"{location}, {neighbors.Count} neighbor{(neighbors.Count == 1 ? "" : "s")}, dist: {distance ?? -1}"; public override string ToString() => $"{location}, {neighbors.Count} neighbor{(neighbors.Count == 1 ? "" : "s")}, dist: {distance ?? -1}";
} }
@ -111,17 +109,16 @@ internal class Day12 : Day
} }
heights[x][y] = ch - 'a'; heights[x][y] = ch - 'a';
allNodes.Add(new DNode(new point(x, y)));
} }
} }
startNode = new DNode(start!); startNode = new DNode(start!);
allNodes.Add(startNode);
var unvisited = new HashSet<DNode>(allNodes); var unvisited = new HashSet<DNode>(allNodes);
DNode? current = startNode; DNode? current = startNode;
while (current != null) while (current != null)
{ {
var currHeight = heights[current.location.x][current.location.y];
foreach (var offset in offsets) foreach (var offset in offsets)
{ {
var test = current.location + offset; var test = current.location + offset;
@ -130,18 +127,8 @@ internal class Day12 : Day
continue; continue;
} }
var testHeight = heights[test.x][test.y]; var found = allNodes.First(n => n.location == test);
if (testHeight <= currHeight + 1) current.neighbors.Add(found);
{
var found = allNodes.FirstOrDefault(n => n.location == test);
if (found == null)
{
found = new DNode(test);
allNodes.Add(found);
unvisited.Add(found);
}
current.neighbors.Add(found);
}
} }
unvisited.Remove(current); unvisited.Remove(current);
@ -151,10 +138,8 @@ internal class Day12 : Day
goalNode = allNodes.First(n => n.location == goal!); goalNode = allNodes.First(n => n.location == goal!);
} }
private long? FindDistance(DNode from, DNode to) private void DijkstraFlood(DNode from, DNode to, bool reverse = false)
{ {
long? distance = null;
allNodes.ForEach(n => allNodes.ForEach(n =>
{ {
n.distance = null; n.distance = null;
@ -165,7 +150,19 @@ internal class Day12 : Day
DNode? current = from; DNode? current = from;
while (current != null) while (current != null)
{ {
foreach (var n in current.neighbors.Where(n => unvisited.Contains(n))) var currentHeight = heights![current.location.x][current.location.y];
var unvisitedNeighbors = current.neighbors.Where(n => unvisited.Contains(n));
if (!reverse)
{
unvisitedNeighbors =
unvisitedNeighbors.Where(n => heights[n.location.x][n.location.y] <= currentHeight + 1);
}
else
{
unvisitedNeighbors =
unvisitedNeighbors.Where(n => currentHeight <= heights[n.location.x][n.location.y] + 1);
}
foreach (var n in unvisitedNeighbors)
{ {
if (n.distance == null || n.distance > current.distance + 1) if (n.distance == null || n.distance > current.distance + 1)
{ {
@ -176,36 +173,23 @@ internal class Day12 : Day
unvisited.Remove(current); unvisited.Remove(current);
if (current == to) if (current == to)
{ {
distance = current.distance;
break; break;
} }
current = unvisited.MinBy(n => n.distance); current = unvisited.MinBy(n => n.distance);
} }
return distance;
} }
internal override string Part1() internal override string Part1()
{ {
var dist = FindDistance(startNode!, goalNode!); DijkstraFlood(startNode!, goalNode!);
return $"Shortest distance from start to end: <+white>{goalNode!.distance}";
return $"Shortest distance from start to end: <+white>{dist ?? -1}";
} }
internal override string Part2() internal override string Part2()
{ {
long? shortestDist = null; DijkstraFlood(goalNode!, startNode!, true);
var aNodes = allNodes.Where(n => heights![n.location.x][n.location.y] == 0); var shortest = allNodes.Where(n => n.distance != null && heights![n.location.x][n.location.y] == 0).MinBy(n => n.distance);
foreach (var n in aNodes) return $"Shortest distance from any minimum-height point to end: <+white>{shortest!.distance}";
{
var dist = FindDistance(n, goalNode!);
if (shortestDist == null || dist < shortestDist)
{
shortestDist = dist;
}
}
return $"Shortest distance from any minimum-height point to end: <+white>{shortestDist}";
} }
} }