[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;
    }
}

原文地址:https://www.cnblogs.com/lz87/p/11547316.html

时间: 2024-08-02 10:29:35

[Daily Coding Problem 294] Shortest round route with rising then falling elevations的相关文章

[Daily Coding Problem] 1 (LeetCode 1). Find if two numbers in an array add up to k

This problem was recently asked by Google. Given a list of numbers and a number k, return whether any two numbers from the list add up to k. For example, given [10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17. Bonus: Can you do this in one

[Daily Coding Problem 68] Count Pairs of attacking bishop pairs

This problem was asked by Google. On our special chessboard, two bishops attack each other if they share the same diagonal. This includes bishops that have another bishop located between them, i.e. bishops can attack through pieces. You are given N b

[Daily Coding Problem 70] Nth perfect number

This problem was asked by Microsoft. A number is considered perfect if its digits sum up to exactly 10. Given a positive integer n, return the n-th perfect number. For example, given 1, you should return 19. Given 2, you should return 28. This is one

Daily Coding Problem: Problem #315

/** * This problem was asked by Google. In linear algebra, a Toeplitz matrix is one in which the elements on any given diagonal from top left to bottom right are identical. Here is an example: 1 2 3 4 8 5 1 2 3 4 4 5 1 2 3 7 4 5 1 2 Write a program t

[Daily Coding Problem 223] O(1) space in order traversal of a binary tree

Typically, an implementation of in-order traversal of a binary tree has O(h) space complexity, where h is the height of the tree. Write a program to compute the in-order traversal of a binary tree using O(1) space. In-order traversal without recursio

Daily Coding Problem: Problem #339

/** * This problem was asked by Microsoft. Given an array of numbers and a number k, determine if there are three entries in the array which add up to the specified number k. For example, given [20, 303, 3, 4, 25] and k = 49, return true as 20 + 4 +

[Daily Coding Problem 290] Quxes Transformation

On a mysterious island there are creatures known as Quxes which come in three colors: red, green, and blue. One power of the Qux is that if two of them are standing next to each other, they can transform into a single creature of the third color. Giv

[Daily Coding Problem] Find the total number of solutions of a linear equation of n variables

Given a linear equation of n variables, find the total number of non-negative integer solutions of it. All coefficients are positive. Example: input: x + 2 * y = 5 output: 3,  the 3 possible integer solutions are x = 1, y = 2; x = 3, y = 1; x = 5, y

[Daily Coding Problem 250] Cryptarithmetic Puzzle

A cryptarithmetic puzzle is a mathematical game where the digits of some numbers are represented by letters. Each letter represents a unique digit. For example, a puzzle of the form: SEND + MORE -------- MONEY may have the solution: {'S': 9, 'E': 5,