zoukankan      html  css  js  c++  java
  • [Daily Coding Problem 294] Shortest round route with rising then falling elevations

    A competitive runner would like to create a route that starts and ends at his house, with the condition that the route goes entirely uphill at first, and then entirely downhill.

    Given a dictionary of places of the form {location: elevation}, and a dictionary mapping paths between some of these locations to their corresponding distances, find the length of the shortest route satisfying the condition above. Assume the runner's home is location 0.

    For example, suppose you are given the following input:

    elevations = {0: 5, 1: 25, 2: 15, 3: 20, 4: 10}
    paths = {
        (0, 1): 10,
        (0, 2): 8,
        (0, 3): 15,
        (1, 3): 12,
        (2, 4): 10,
        (3, 4): 5,
        (3, 0): 17,
        (4, 0): 10
    }
    

    In this case, the shortest valid path would be 0 -> 2 -> 4 -> 0, with a distance of 28.

    Solution 1. Dijkstra's algorithm to compute shortest path from a single source

    1. Construct two subgraphs, one with only rising edges, the other one with only falling edges. For the falling edges only graph, revert all edges' direction so we can apply the Dijkstra's single source shortest path algorithm.

    2. Apply Dijkstra's algorithm on rising-edge subgraph to compute the shortest path from location 0 to all other locations on rising-only edges;  Then apply Dijkstra's algorithm again on falling-edge subgraph to compute the shortest path from all other locations to location 0 on falling-only edges. Denote these two results as rising[] and falling[].

    3. Iterate through all other locations on rising[] and falling[] and find the minimum sum.

    The runtime is O(V + E * log E), space is O(V + E)

    public class ShortestRouteWithRestriction {
        public static int shortestRouteWithRestriction(int[] elevations, int[][] paths) {
            int n = elevations.length;
            int[] rising = new int[n];
            int[] falling = new int[n];
            Arrays.fill(rising, Integer.MAX_VALUE);
            Arrays.fill(falling, Integer.MAX_VALUE);
    
            Map<Integer, List<int[]>> risingGraph = new HashMap<>();
            Map<Integer, List<int[]>> fallingGraph = new HashMap<>();
            for(int i = 0; i < n; i++) {
                risingGraph.put(i, new ArrayList<>());
                fallingGraph.put(i, new ArrayList<>());
            }
    
            //construct graph with only rising/falling paths
            for(int i = 0; i < paths.length; i++) {
                if(paths[i][1] > paths[i][0]) {
                    risingGraph.get(paths[i][0]).add(new int[]{paths[i][1], paths[i][2]});
                }
                else if(paths[i][1] < paths[i][0]) {
                    fallingGraph.get(paths[i][1]).add(new int[]{paths[i][0], paths[i][2]});
                }
            }
            dijkstra(risingGraph, rising, 0);
            dijkstra(fallingGraph, falling, 0);
    
            int res = Integer.MAX_VALUE;
            for(int i = 1; i < n; i++) {
                if(rising[i] < Integer.MAX_VALUE && falling[i] < Integer.MAX_VALUE) {
                    res = Math.min(res, rising[i] + falling[i]);
                }
            }
            return res;
        }
        private static void dijkstra(Map<Integer, List<int[]>> g, int[] distance, int startNode) {
            boolean[] processed = new boolean[distance.length];
            distance[startNode] = 0;
    
            PriorityQueue<int[]> minPq = new PriorityQueue<>((a1, a2) -> {return a1[0] - a2[0];});
            minPq.add(new int[]{0, startNode});
            while(!minPq.isEmpty()) {
                int[] curr = minPq.poll();
                int currNodeDistance = curr[0];
                int currNodeLabel = curr[1];
                if(processed[currNodeLabel]) {
                    continue;
                }
                processed[currNodeLabel] = true;
                for(int[] edge : g.get(currNodeLabel)) {
                    int neighborNodeLabel = edge[0];
                    int weight = edge[1];
                    if(currNodeDistance + weight < distance[neighborNodeLabel]) {
                        distance[neighborNodeLabel] = currNodeDistance + weight;
                        minPq.add(new int[]{distance[neighborNodeLabel], neighborNodeLabel});
                    }
                }
            }
        }
        public static void main(String[] args) {
            int[] elevations = {5,25,15,20,10};
            int[][] paths = {{0, 1, 10}, {0, 2, 8}, {0, 3, 15}, {1, 3, 12}, {2, 4, 10}, {3, 4, 5}, {3, 0, 17}, {4, 0, 10}};
            System.out.println(shortestRouteWithRestriction(elevations, paths));
        }
    }

    Solution 2. Topological Sort to compute shortest path from a single source

    Similar with solution 1, we still divide the original problem into two subproblems. But we can compute the uphill only and downhill only distances more efficiently. If we only consider only uphill or only downhill path segments, it will be impossible to form a cycle, so we are dealing with a DAG. As a result, for each of these two subproblems, we can use a topological sort to determine in what order to visit the locations starting from 0.(for downhill, we revert the edge directions just like solution 1). Then we use this ordering to find the minimum cost path for each subproblem. 

    Both the runtime and space complexity are O(V + E).

    public class ShortestRouteWithRestriction {
        public static int shortestRouteWithRestriction(int[] elevations, int[][] paths) {
            int n = elevations.length;
            Map<Integer, List<int[]>> risingGraph = new HashMap<>();
            Map<Integer, List<int[]>> fallingGraph = new HashMap<>();
            for(int i = 0; i < n; i++) {
                risingGraph.put(i, new ArrayList<>());
                fallingGraph.put(i, new ArrayList<>());
            }
    
            //construct graph with only rising/falling paths
            for(int i = 0; i < paths.length; i++) {
                if(paths[i][1] > paths[i][0]) {
                    risingGraph.get(paths[i][0]).add(new int[]{paths[i][1], paths[i][2]});
                }
                else if(paths[i][1] < paths[i][0]) {
                    fallingGraph.get(paths[i][1]).add(new int[]{paths[i][0], paths[i][2]});
                }
            }
            Stack<Integer> risingOrder = topologicalSort(risingGraph, 0);
            Stack<Integer> fallingOrder = topologicalSort(fallingGraph, 0);
    
            int[] rising = getDistance(risingGraph, risingOrder);
            int[] falling = getDistance(fallingGraph, fallingOrder);
            int res = Integer.MAX_VALUE;
            for(int i = 1; i < n; i++) {
                if(rising[i] < Integer.MAX_VALUE && falling[i] < Integer.MAX_VALUE) {
                    res = Math.min(res, rising[i] + falling[i]);
                }
            }
            return res;
        }
    
        private static Stack<Integer> topologicalSort(Map<Integer, List<int[]>> g, int startNode) {
            boolean[] visited = new boolean[g.size()];
            Stack<Integer> stack = new Stack<>();
            topologicalSortHelper(g, startNode, visited, stack);
            return stack;
        }
    
        private static void topologicalSortHelper(Map<Integer, List<int[]>> g, int currNode, boolean[] visited, Stack<Integer> stack) {
            if(!visited[currNode]) {
                for(int[] edge : g.get(currNode)) {
                    topologicalSortHelper(g, edge[0], visited, stack);
                }
                visited[currNode] = true;
                stack.push(currNode);
            }
        }
    
        private static int[] getDistance(Map<Integer, List<int[]>> g, Stack<Integer> order) {
            int[] distance = new int[g.size()];
            Arrays.fill(distance, Integer.MAX_VALUE);
            distance[0] = 0;
            while(!order.isEmpty()) {
                int curr = order.pop();
                for(int[] edge : g.get(curr)) {
                    distance[edge[0]] = Math.min(distance[edge[0]], distance[curr] + edge[1]);
                }
            }
            return distance;
        }
    }
  • 相关阅读:
    P2280 [HNOI2003]激光炸弹[前缀和]
    P1280 尼克的任务[区间覆盖dp]
    P1352 没有上司的舞会[树形dp]
    HDU1024 Max Sum Plus Plus[DP]
    P1282 多米诺骨牌[可行性01背包]
    P1063 能量项链[区间DP]
    P1880 [NOI1995]石子合并[环形DP]
    P1091 合唱队形[单调性+DP]
    Gym 100971D 单调栈
    Gym 100971A Treasure Island BFS 思维题
  • 原文地址:https://www.cnblogs.com/lz87/p/11547316.html
Copyright © 2011-2022 走看看