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;
        }
    }
  • 相关阅读:
    第三话-单一职责原则
    2014辽宁省赛 Repeat Number
    【iOS】Swift字符串截取方法的改进
    Android中特殊图形的生成样例
    Tiny server:小型Web服务器
    C语言复合字面量的使用
    浅析数据库连接池(一)
    答复学习汇编不顺利的准大学生
    Struts2拦截器
    7.数据本地化CCString,CCArray,CCDictionary,tinyxml2,写入UserDefault.xml文件,操作xml,解析xml
  • 原文地址:https://www.cnblogs.com/lz87/p/11547316.html
Copyright © 2011-2022 走看看