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;
        }
    }
  • 相关阅读:
    WebService之使用CXF偶遇的BUG
    修改Web项目的名称后,在TomCat中部署项目,项目的名称还是与原来相同的解决方案
    JAVA面试题一 之 io流的操作
    第四天 字符串的格式化输出
    第三天 while循环 及其用法
    第二天 注释 、运算符,文件扩展名
    第一天 python入门 基础 “”“Hello World”和if-elif的使用、数据类型
    62. (待补)统计一个字符在字符串中出现次数 与 统计一个字符串在另一字符串中出现次数。
    61. 解析linux密码配置文件,获取配置文件信息,每行归纳为一个指针数组存入内存, 练习文件,数组,指针,字符串处理相关的配合使用要点
    60. 去字符串中的空格,去左空格,去右空格,去全部空格。
  • 原文地址:https://www.cnblogs.com/lz87/p/11547316.html
Copyright © 2011-2022 走看看