leetcode787 - Cheapest Flights Within K Stops - medium

There are n cities connected by m flights. Each fight starts from city u and arrives at v with a price w.
Now given all the cities and fights, together with starting city src and the destination dst, your task is to find the cheapest price from src to dst with up to k stops. If there is no such route, output -1.
Example 1:
Input:
n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
src = 0, dst = 2, k = 1
Output: 200
Explanation:
The graph looks like this:

The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture.

 

1.DFS with prune。
函数头private void dfs(int src, int k, int sum, Set<Integer> visited), 共用变量有K, dst, Map<Integer, Map<Integer, Integer>> costs. 说明从该src开始,根据当前机场可以飞的航班试着走到下一个城市,等到走到终点的情况下说明成功,看一下这种路径机票价格有没有更低。
1.如果发现已经走到dst了,可以去全局打擂台了。
2.一些情况没有必要继续dfs下去,退出:如果现在没有以src开头的航班;如果src已经被访问过了不要循环,如果已经超过K+1次飞行次数,如果当前sum已经比之前计算过的成功sum高了(剪枝)
3.对当前机场可以飞的各个城市去递归一下。递归前后给isVisited状态变一变。

 

2.DP。
dp[k][i]定义: 从src开始,飞 k 次到 i 机场最低要多少钱。
状态转移方程:dp[k][i] = min(dp[k-1][i], dp[k-1][u] + cost[u][i])。
初始化:dp[0][src] = 0;
扫描方式:因为递推依赖k-1,外层按k扫,先知道飞1次的答案,再知道飞2345次的。内层按航班信息扫。
返回结果:dp[K + 1][dst]

细节:
1.状态转移方程解释:u为所有可以飞到i的城市。那么你要飞到i,要么是之前少飞一些次数到i的答案,要么是先少飞一些次数到另一个城市u,再从u转机到i的答案。
2.不能用Arrays.fill()初始化二维数组,只可以一维数组。

 

实现1:DFS。

class Solution {
    private int minSum = Integer.MAX_VALUE;
    private int K;
    private int dst;
    private Map<Integer, Map<Integer, Integer>> costs;
    
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
        this.K = K;
        this.dst = dst;
        this.costs = generateMap(flights);
        dfs(src, 0, 0, new HashSet<Integer>());
        return minSum == Integer.MAX_VALUE ? -1 : minSum;
    }
    
    private void dfs(int src, int k, int sum, Set<Integer> visited) {
        if (src == dst) {
            minSum = Math.min(minSum, sum);
        }
        
        // P1: 做一个减枝DFS才能不TLE,当你现在已经sum >= minSum的时候,就没必要继续dfs下去了,没希望了。
        if (!costs.containsKey(src) || visited.contains(src) || k == K + 1 || sum >= minSum) {
            return;
        }
        
        Map<Integer, Integer> map = costs.get(src);
        visited.add(src);
        for (int next : map.keySet()) {
            dfs(next, k + 1, sum + map.get(next), visited);
        }
        visited.remove(src);
    }
    
    private Map<Integer, Map<Integer, Integer>> generateMap(int[][] flights) {
        Map<Integer, Map<Integer, Integer>> ans = new HashMap<>();
        for (int i = 0; i < flights.length; i++) {
            ans.putIfAbsent(flights[i][0], new HashMap<Integer, Integer>());
            ans.get(flights[i][0]).put(flights[i][1], flights[i][2]);
        }
        return ans;
    }
}

 

实现2:

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
        // dp[k][i]: 从src开始,飞 k 次到 i 机场最低要多少钱。最后返回结果就是dp[K + 1][dst]
        int[][] dp = new int[K + 2][n];
        // P1: 要初始化。而且Arrays.fill()只能对一维数组,不可以对二维数组。
        for (int i = 0; i < dp.length; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        
        dp[0][src] = 0;
        for (int k = 1; k < K + 2; k++) {
            for (int i = 0; i < flights.length; i++) {
                int start = flights[i][0], end = flights[i][1], price = flights[i][2];
                dp[k][end] = Math.min(dp[k][end], dp[k - 1][end]);
                if (dp[k - 1][start] != Integer.MAX_VALUE) {
                    dp[k][end] = Math.min(dp[k][end], dp[k - 1][start] + price);
                }
            }
        }
        return dp[K + 1][dst] == Integer.MAX_VALUE ? -1 : dp[K + 1][dst];
    }
}

 

posted @ 2018-09-18 05:04  jasminemzy  阅读(241)  评论(0编辑  收藏  举报