leetcode
leetcode 1401 ~ 1450
统计所有可行路径

统计所有可行路径

难度:

标签:

题目描述

代码结果

运行时间: 145 ms, 内存: 17.2 MB


/*
 * 题目思路:
 * 使用 Java Stream 的方法来解决问题。我们可以利用 Stream API 来简化动态规划的过程。
 * 我们需要将动态规划转换为一个流处理过程,通过多次 map 和 reduce 操作来更新和计算路径数。
 */

import java.util.stream.IntStream;

public class SolutionStream {
    private static final int MOD = 1000000007;
    
    public int countRoutes(int[] locations, int start, int finish, int fuel) {
        int n = locations.length;
        int[][] dp = new int[n][fuel + 1];
        dp[start][fuel] = 1;
        
        IntStream.rangeClosed(0, fuel).map(remainingFuel -> fuel - remainingFuel).forEach(remainingFuel -> {
            IntStream.range(0, n).filter(currentCity -> dp[currentCity][remainingFuel] > 0).forEach(currentCity -> {
                IntStream.range(0, n).filter(nextCity -> currentCity != nextCity).forEach(nextCity -> {
                    int cost = Math.abs(locations[currentCity] - locations[nextCity]);
                    if (remainingFuel >= cost) {
                        dp[nextCity][remainingFuel - cost] = (dp[nextCity][remainingFuel - cost] + dp[currentCity][remainingFuel]) % MOD;
                    }
                });
            });
        });
        
        return IntStream.rangeClosed(0, fuel).map(remainingFuel -> dp[finish][remainingFuel]).reduce(0, (a, b) -> (a + b) % MOD);
    }
}

解释

方法:

这个题解采用动态规划的方法来解决。首先对城市位置进行排序,并且重新定位起始和结束城市的索引。这样可以利用位置的有序性来简化状态转移。定义两个动态规划数组 dpL 和 dpR,分别表示从左向右和从右向左计算到达某个城市的路径数。dpL[i][f] 表示从起点到位置i使用f单位燃料的路径数,dpR[i][f] 则是相反方向的计数。通过遍历每一步可能使用的燃料量和城市,我们更新两个DP数组。这种方法不断地根据前一个城市的状态来更新当前城市的状态,利用了前后城市距离的具体值来减少燃料,并在途中累加可能的路径。最后,将两个DP数组在目标城市的所有燃料状态下的值相加,得到最终结果。如果起始城市和目标城市是同一个,则还需调整最终计数,因为算法中计算了从起点到起点的一个额外路径。

时间复杂度:

O(n * f)

空间复杂度:

O(n * f)

代码细节讲解

🦆
动态规划数组 dpL 和 dpR 的设计是如何确保能够覆盖从任一起点到任一终点的所有可能路径的?
动态规划数组 dpL 和 dpR 通过逐步计算每种燃料使用情况下从起点到各个城市的路径数量,确保了能够覆盖从任一起点到任一终点的所有可能路径。dpL 从左向右计算,负责记录从起点开始向右移动到达每个城市时各种燃料情况的路径数量;dpR 从右向左计算,记录从起点开始向左移动到达每个城市时的路径数量。这样,无论目标城市在起点的左边还是右边,都有相应的动态规划数组来记录到达该城市的所有可能路径数。
🦆
题解中提到,如果起始城市和目标城市是同一个,则需要调整最终计数,具体是如何调整的?为什么要进行这样的调整?
如果起始城市和目标城市是同一个,算法实际上会在初始化时将 dpL[start][0] 和 dpR[start][0] 设为 1,意味着考虑了从起点到起点的路径。因此,当最后统计所有路径时,这一路径会被重复计算一次。为了纠正这一重复计数,需要在最终结果中减去1。这样调整是为了确保路径计数的准确性,避免因初始化导致的重复计数问题。
🦆
在更新 dpL 和 dpR 数组时,为什么要同时考虑从左到右和从右到左的情况?这种双向更新是如何帮助解题的?
在更新 dpL 和 dpR 数组时,同时考虑从左到右和从右到左的情况能够有效处理城市间的双向移动。这种双向更新允许算法在任一方向上捕捉路径,并根据当前城市和燃料消耗来调整下一个可能的移动方向。例如,当城市间距离较小并且剩余燃料较多时,可以通过双向更新来探索更多的路径可能性。这种方法增强了动态规划的灵活性和覆盖面,使其能够更好地适应不同的燃料和距离情况,从而找到所有可能的到达路径。
🦆
题解中未详细说明如何处理边界条件,比如当燃料为0或非常少时的情况。这种情况下算法如何确保正确性?
在燃料为0或非常少的情况下,动态规划的设计保证了算法的正确性。dpL 和 dpR 在初始化时,将 dpL[start][0] 和 dpR[start][0] 设置为 1,表明无需消耗燃料即可停留在起点。对于其他城市,如果没有足够的燃料到达某个城市,则对应的 dpL[i][f] 和 dpR[i][f] 保持为0,表示无法到达。这种处理确保了即使在燃料非常少的情况下,算法也能正确反映从起点到其他城市的可达性。

相关问题