leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结相关的知识,希望对你有一定的参考价值。

leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结

最短路:给定两个顶点,在以这两个顶点为起点和终点的路径中,边的权值和最小的路径。

最短路径中有几种经典的算法,我们主要练习的是Dijkstra算法和Floyd算法,分别用于解决单元最短路径问题和多源最短路径问题。

Dijkstra算法适用于单源最短路径,具体地,找到最短路已经确定的节点,从它出发更新相邻节点的最短距离。

Floyd算法适用于多源最短路径,具体地,通过一系列n阶矩阵来计算一个n节点加权图的最短距离。每轮让第k个节点做中转节点,更新第i个节点到第j个节点的最短路径。

动态规划常常适用于有重叠子问题和最优子结构性质的问题,并且记录所有子问题的结果,因此动态规划方法所耗时间往往远少于朴素解法。

动态规划有自底向上和自顶向下两种解决问题的方式。自顶向下即记忆化递归,自底向上就是递推。

记忆化即记忆化搜索,也叫记忆化递归,其实就是拆分子问题的时候,发现有很多重复子问题,然后再求解它们以后记录下来。以后再遇到要求解同样规模的子问题的时候,直接读取答案。

使用动态规划解决的问题有个明显的特点,一旦一个子问题的求解得到结果,以后的计算过程就不会修改它,这样的特点叫做无后效性,求解问题的过程形成了一张有向无环图。动态规划只解决每个子问题一次,具有天然剪枝的功能,从而减少计算量。

深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次.

广度优先搜索算法(Breadth-First Search,缩写为 BFS),又称为宽度优先搜索,是一种图形搜索算法。简单的说,BFS 是从根结点开始,沿着树的宽度遍历树的结点。如果所有结点均被访问,则算法中止。

1-电动车游城市
题目链接:题目链接戳这里!!!

思路:Dijkstra算法
对paths数组建立邻接表,分别存储两个相邻节点以及节点之间的权值,ans数组记录到达当前节点的最少用时,优先队列中存储所用时间,当前位置,剩余电量,并且按照所用时间升序排序,保证每次选择的都是所用时间最少的,如果电量未满可以考虑充电,如如果剩余电量够,可以往相邻的节点走。

class Solution 
    public int electricCarPlan(int[][] paths, int cnt, int start, int end, int[] charge) 
        int n = charge.length ;
        List<int[]> [] g = new List[n] ;

        for(int i=0; i<n; i++)
            g[i] = new ArrayList<int[]>() ;
        

        for(int [] path : paths) //建立邻接表,后面好操作
            int u = path[0], v = path[1], w = path[2] ;
            g[u].add(new int [] v, w) ;
            g[v].add(new int [] u, w) ;
        

        int [][] ans = new int [n][cnt+1] ; //当前位置n,剩余电量cnt,时候的最少用时
        for(int i=0; i<n; i++)
            Arrays.fill(ans[i], Integer.MAX_VALUE/2) ;
        
        ans[start][0] = 0 ;

        PriorityQueue<int []> queue = new PriorityQueue<>((a,b)->(a[0]-b[0])) ;
        queue.add(new int [] 0, start, 0) ; //数组中三个元素分别为已用时间,当前位置,剩余电量

        while(!queue.isEmpty())
            int [] p = queue.poll() ;
            int time = p[0], cur = p[1], power = p[2] ;

            if(time > ans[cur][power])
                continue ;
            
            if(cur == end)
                return time ;
            
            if(power<cnt)//电量未满,可以考虑充电
                int t = time + charge[cur] ;
                if(t<ans[cur][power+1])
                    ans[cur][power+1] = t ;
                    queue.add(new int [] t, cur, power+1) ;
                
            

            for(int [] path : g[cur])
                int next = path[0] ;
                int cost = path[1] ;
                int t = time + cost ;
                int pow = power - cost ;
                if(pow>=0 && t<ans[next][pow])
                    ans[next][pow] = t ;
                    queue.add(new int [] t, next, pow) ;
                
            
        
        return -1 ;
    


2-K站中转内最便宜的航班
题目链接:题目链接戳这里!!!

思路1:记忆化搜索(dfs)
从src出发,搜索所有能到dst的,并且中转个数少于k的路径,找出价格最便宜的路径价格。

用memo[i][k]做记忆化,防止重复计算,该数组表示从i点到达终点经历不超过k次中转的最低价格。

class Solution 
    int INF = 1000000007 ;
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) 
        //记忆化搜索
        //memo[i][k]数组记录从当前顶点i出发经过最多k个中转点到最后的顶点的最便宜的价格
        int [][] memo = new int [n][k+2] ;

        int ans = dfs(flights, src, dst, k+1, memo) ;

        return ans>=INF ? -1 : ans ;
    

    public int dfs(int [][] flights, int i, int end, int k, int [][] memo)
        if(k<0)
            return INF ;
        
        if(i==end)
            return 0 ;
        
        if(memo[i][k] != 0)
            return memo[i][k] ;
        

        int min = INF ;
        for(int [] flight : flights)
            if(flight[0]==i)
                min = Math.min(min, dfs(flights, flight[1], end, k-1, memo) + flight[2]) ;
            
        
        memo[i][k] = min ;
        return min ;

    


思路2:bfs+剪支
对于当前节点,遍历相邻节点,每一轮bfs,k减少1,如果找到更小的价格,则更新ans数组。

class Solution 
    int INF = 1000000007 ;
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) 
       //bfs+剪支
       List<int []> [] g = new List[n] ;
       for(int i=0; i<n; i++)
           g[i] = new ArrayList<>() ;
       

       for(int [] flight : flights)
           int u = flight[0], v = flight[1], w=flight[2] ;
           g[u].add(new int [] v, w) ;
       

       int [] ans = new int [n] ;
       Arrays.fill(ans, Integer.MAX_VALUE) ;
       Queue<int []> queue = new LinkedList<>() ;
       queue.add(new int [] src, 0) ;

       while(!queue.isEmpty() && k+1>0)
           int size = queue.size() ;
           for(int i=0; i<size; i++)
               int [] p = queue.poll() ;
               for(int [] f : g[p[0]])
                   int price = p[1] + f[1];
                   if(price < ans[f[0]])
                       ans[f[0]] = price ;
                       if(f[0] != dst)
                       queue.add(new int [] f[0], price) ;
                       
                   
               
           
           k -- ;
       
       return ans[dst] >= INF ? -1 : ans[dst] ;

    


思路3:动态规划
其实就是记忆化递归的转换,自顶向下变成自底向上。

class Solution 
    int INF = 1000000007 ;
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) 
     //动态规划
     //dp[i][k]表示从i点出发到dst走最多k+1步的最少价格
     int [][] dp = new int [n][k+2] ;
     for(int i=0; i<n; i++)
     Arrays.fill(dp[i],INF) ;
     return f(n, flights, src, dst, k+1, dp) ;
    
    public int f(int n, int [][] flights, int src, int dst, int K, int [][] dp)
        dp[dst][0] = 0 ;

        for(int k=1; k<=K; k++)
            for(int [] flight : flights)
                dp[flight[0]][k] = Math.min(dp[flight[0]][k], dp[flight[1]][k-1] + flight[2]) ;
            
        
        int ans =  IntStream.of(dp[src]).min().getAsInt() ;

        return ans >= INF ? -1 : ans ;
    

3-阈值距离内邻居最少的城市
题目链接:题目据链接戳这里!!!

思路:首先要求出来每个节点到其余节点的最短路径,所以可以使用Floyd算法,使用matrix矩阵记录从i节点到j节点的最短路长度,然后遍历所有i节点,如果到达j节点的距离小于等于distanceThreshold,则使用nums[i]记录满足要求的路径数量,选择最小值。

class Solution 
    public int findTheCity(int n, int[][] edges, int distanceThreshold) 
        //多源最短路径,使用Floyd算法
        int [][] matrix = new int [n][n] ; 
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                matrix[i][j] = 1000000000 ;
            
        

        for(int i=0; i<n; i++)
            matrix[i][i] = 0 ;
        

        for(int [] edge : edges)
            int u = edge[0], v = edge[1], w = edge[2] ;
            matrix[u][v] = matrix[v][u] = w ;
        

        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                    matrix[i][j] = Math.min(matrix[i][j], matrix[i][k]+matrix[k][j]) ;
                
            
        

        int [] num = new int [n] ;
        int pos = -1, min = 1000000000 ;

        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                if(matrix[i][j]<=distanceThreshold)
                    num[i] ++ ;
                
            
            if(num[i] <= min)
                min = num[i] ;
                pos = i ;
            
        

        return pos ;
    


**

不辜负每一份热情,不讨好每一份冷漠,做好自己,健健康康为祖国工作50年!!!

**

以上是关于leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode1293网格中的最短路径(DFS和BFS)分析

LeetCode刷题之BFS和DFS

leetcode之最短路刷题总结1

最短路径:DFS、BFS 还是两者兼而有之?

模板 图的遍历 bfs+dfs 图的最短路径 Floyed+Dijkstra

动画演示广度优先算法寻找最短路径