Advertisement
Guest User

Untitled

a guest
Jan 3rd, 2025
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.05 KB | None | 0 0
  1. enum ReindeerDirection { NORTH, SOUTH, EAST, WEST }
  2.  
  3. class MazeState {
  4.     private int points;
  5.     private final Point location;
  6.     private final ReindeerDirection direction;
  7.  
  8.     public MazeState(Point location, ReindeerDirection direction, int points) {
  9.         this.location = location;
  10.         this.direction = direction;
  11.         this.points = points;
  12.     }
  13.  
  14.     public Point getLocation() { return location; }
  15.     public ReindeerDirection getDirection() { return direction; }
  16.     public int getPoints() { return points; }
  17.     public void setPoints(int points) { this.points = points; }
  18.     public String toString() { return this.location.toString(); }
  19.  
  20.     @Override
  21.     public boolean equals(Object o) {
  22.         if (this == o) return true;
  23.         if (o == null || getClass() != o.getClass()) return false;
  24.         MazeState mazeState = (MazeState) o;
  25.         return Objects.equals(location, mazeState.location) && direction == mazeState.direction;
  26.     }
  27.  
  28.     @Override
  29.     public int hashCode() {
  30.         return Objects.hash(location, direction);
  31.     }
  32. }
  33.  
  34. class MazePointComparator implements Comparator<MazeState> {
  35.     @Override
  36.     public int compare(MazeState x, MazeState y) {
  37.         return Integer.compare(x.getPoints(), y.getPoints());
  38.     }
  39. }
  40.  
  41. // Given a maze state, return the next possible maze states. At each coordinate, the reindeer
  42. // can either keep moving in the same direction, or turn 90 degrees. We accumulate the total
  43. // points accrued in the maze state as well.
  44. private static List<MazeState> getNeighbors(char[][] grid, MazeState node) {
  45.     List<MazeState> neighbors = new ArrayList<>();
  46.  
  47.     Point p = node.getLocation();
  48.     ReindeerDirection direction = node.getDirection();
  49.     int points = node.getPoints();
  50.  
  51.     if (direction == ReindeerDirection.NORTH) {
  52.         if (grid[p.x-1][p.y] == '.') {
  53.             neighbors.add(new MazeState(new Point(p.x-1, p.y), direction, points + 1));
  54.         }
  55.         if (grid[p.x][p.y-1] == '.') {
  56.             neighbors.add(new MazeState(p, ReindeerDirection.WEST, points + 1000));
  57.         }
  58.         if (grid[p.x][p.y+1] == '.') {
  59.             neighbors.add(new MazeState(p, ReindeerDirection.EAST, points + 1000));
  60.         }
  61.     } else if (direction == ReindeerDirection.EAST) {
  62.         if (grid[p.x][p.y+1] == '.') {
  63.             neighbors.add(new MazeState(new Point(p.x, p.y+1), direction, points + 1));
  64.         }
  65.         if (grid[p.x-1][p.y] == '.') {
  66.             neighbors.add(new MazeState(p, ReindeerDirection.NORTH, points + 1000));
  67.         }
  68.         if (grid[p.x+1][p.y] == '.') {
  69.             neighbors.add(new MazeState(p, ReindeerDirection.SOUTH, points + 1000));
  70.         }
  71.     } else if (direction == ReindeerDirection.SOUTH) {
  72.         if (grid[p.x+1][p.y] == '.') {
  73.             neighbors.add(new MazeState(new Point(p.x+1, p.y), direction, points + 1));
  74.         }
  75.         if (grid[p.x][p.y-1] == '.') {
  76.             neighbors.add(new MazeState(new Point(p.x, p.y), ReindeerDirection.WEST, points + 1000));
  77.         }
  78.         if (grid[p.x][p.y+1] == '.') {
  79.             neighbors.add(new MazeState(new Point(p.x, p.y), ReindeerDirection.EAST, points + 1000));
  80.         }
  81.     } else if (direction == ReindeerDirection.WEST) {
  82.         if (grid[p.x][p.y-1] == '.') {
  83.             neighbors.add(new MazeState(new Point(p.x, p.y-1), direction, points + 1));
  84.         }
  85.         if (grid[p.x-1][p.y] == '.') {
  86.             neighbors.add(new MazeState(new Point(p.x, p.y), ReindeerDirection.NORTH, points + 1000));
  87.         }
  88.         if (grid[p.x+1][p.y] == '.') {
  89.             neighbors.add(new MazeState(new Point(p.x, p.y), ReindeerDirection.SOUTH, points + 1000));
  90.         }
  91.     }
  92.  
  93.     return neighbors;
  94. }
  95.  
  96. // Part 1: Run Dijkstra's algorithm on the maze states, not the coordinates of the grid.
  97. private static int part1(char[][] grid, Point start, Point end) {
  98.     Set<MazeState> visited = new HashSet<>();
  99.     PriorityQueue<MazeState> pq = new PriorityQueue<>(new MazePointComparator());
  100.  
  101.     MazeState initial = new MazeState(start, ReindeerDirection.EAST, 0);
  102.     pq.add(initial);
  103.  
  104.     while (!pq.isEmpty()) {
  105.         MazeState node = pq.poll();
  106.         Point p = node.getLocation();
  107.  
  108.         if (visited.contains(node)) continue;
  109.  
  110.         // Because we accumulate the points in the maze state, if we've reached the end,
  111.         // then return the points of the end state.
  112.         if (p.equals(end)) {
  113.             return node.getPoints();
  114.         }
  115.  
  116.         visited.add(node);
  117.  
  118.         List<MazeState> neighbors = getNeighbors(grid, node);
  119.  
  120.         for (MazeState neighbor : neighbors) {
  121.             if (!visited.contains(neighbor)) {
  122.                 pq.add(neighbor);
  123.             }
  124.         }
  125.     }
  126.  
  127.     return 0;
  128. }
  129.  
  130. // Part 2: Similar to part 1, except now we don't just stop once we reach the end state.
  131. // We keep processing potential branching paths until all meaningful nodes have been visited.
  132. // We maintain a map of best known costs to reach a given maze state.
  133. // We also maintain a map of parent maze states, so we can reconstruct the path.
  134. private static int part2(char[][] grid, Point start, Point end) {
  135.     Map<MazeState, Integer> costs = new HashMap<>(); // maps the best known costs to reach a given maze state so far
  136.     Set<MazeState> visited = new HashSet<>();
  137.     PriorityQueue<MazeState> pq = new PriorityQueue<>(new MazePointComparator());
  138.     // for each maze state key, tracks a list of parent maze states that could get us to that maze state.
  139.     Map<MazeState, List<MazeState>> parents = new HashMap<>();
  140.  
  141.     MazeState initial = new MazeState(start, ReindeerDirection.EAST, 0);
  142.     pq.add(initial);
  143.     costs.put(initial, 0);
  144.  
  145.     while (!pq.isEmpty()) {
  146.         MazeState node = pq.poll();
  147.  
  148.         if (visited.contains(node)) continue;
  149.  
  150.         visited.add(node);
  151.  
  152.         List<MazeState> neighbors = getNeighbors(grid, node);
  153.  
  154.         // For each neighbor, look in our costs hashmap for the best known cost to get to that neighbor so far.
  155.         // If we then calculate that the best known cost to get to our current node plus the cost it takes
  156.         // to get from our current node to the neighbor is less than the best known cost to get to our neighbor,
  157.         // then update it. In other words, we update if: cost[node] + weight[node, neighbor] < cost[neighbor]
  158.         for (MazeState neighbor : neighbors) {
  159.             if (!visited.contains(neighbor)) {
  160.                 // Check if our costs map contains these maze states. If not, initialize their costs to infinity.
  161.                 if (!costs.containsKey(node)) {
  162.                     costs.put(node, Integer.MAX_VALUE);
  163.                 }
  164.                 if (!costs.containsKey(neighbor)) {
  165.                     costs.put(neighbor, Integer.MAX_VALUE);
  166.                 }
  167.  
  168.                 // One key thing to note here: the cost it takes to get from the current node
  169.                 // to the neighbor is (neighbor.getPoints() - node.getPoints()) because our `getNeighbors()`
  170.                 // calculation accumulates the points.
  171.                 int newCost = costs.get(node) + (neighbor.getPoints() - node.getPoints());
  172.  
  173.                 // If we've found a better cost, update it.
  174.                 if (newCost <= costs.get(neighbor)) {
  175.                     costs.put(neighbor, newCost);
  176.                     pq.add(neighbor);
  177.  
  178.                     // And keep track of where we've come from
  179.                     if (parents.containsKey(neighbor)) {
  180.                         parents.get(neighbor).add(node);
  181.                     } else {
  182.                         List<MazeState> parentsOfNeighbor = new ArrayList<>();
  183.                         parentsOfNeighbor.add(node);
  184.                         parents.put(neighbor, parentsOfNeighbor);
  185.                     }
  186.                 }
  187.             }
  188.         }
  189.     }
  190.  
  191.     Set<Point> uniquePoints = new HashSet<>();
  192.  
  193.     // Find the min points in takes to get to the end.
  194.     int min = Integer.MAX_VALUE;
  195.     for (MazeState state : parents.keySet()) {
  196.         if (state.getLocation().equals(end)) {
  197.             min = Math.min(min, state.getPoints());
  198.         }
  199.     }
  200.  
  201.     // Find each end state that results in the minimum points in reaching it.
  202.     List<MazeState> endStates = new ArrayList<>();
  203.     for (MazeState state : parents.keySet()) {
  204.         if (state.getLocation().equals(end) && state.getPoints() == min) {
  205.             endStates.add(state);
  206.         }
  207.     }
  208.  
  209.     // Iterate through each end state and walk backwards.
  210.     for (MazeState endState: endStates) {
  211.         Stack<MazeState> stack = new Stack<>();
  212.         stack.add(endState);
  213.  
  214.         // Walk backwards from the end state, visiting the parent/previous states that got us to that state
  215.         // and add their points to a set.
  216.         while (!stack.isEmpty()) {
  217.             MazeState curr = stack.pop();
  218.             uniquePoints.add(curr.getLocation());
  219.  
  220.             if (parents.containsKey(curr)) {
  221.                 for (MazeState ms : parents.get(curr)) {
  222.                     stack.push(ms);
  223.                 }
  224.             }
  225.         }
  226.     }
  227.  
  228.     return uniquePoints.size();
  229. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement