题目链接

统计所有可行路径

题目描述

image.png

实现代码

记忆化搜索 + DFS遍历实现代码:

  1. class Solution {
  2. int mod = 1000000007;
  3. // 缓存器:用于记录「特定状态」下的结果
  4. // cache[i][fuel] 代表从位置 i 出发,当前剩余的油量为 fuel 的前提下,到达目标位置的「路径数量」
  5. int[][] cache;
  6. public int countRoutes(int[] ls, int start, int end, int fuel) {
  7. int n = ls.length;
  8. // 初始化缓存器
  9. // 之所以要初始化为 -1
  10. // 是为了区分「某个状态下路径数量为 0」和「某个状态尚未没计算过」两种情况
  11. cache = new int[n][fuel + 1];
  12. for (int i = 0; i < n; i++) {
  13. Arrays.fill(cache[i], -1);
  14. }
  15. return dfs(ls, start, end, fuel);
  16. }
  17. /**
  18. * 计算「路径数量」
  19. * @param ls 入参 locations
  20. * @param u 当前所在位置(ls 的下标)
  21. * @param end 目标哦位置(ls 的下标)
  22. * @param fuel 剩余油量
  23. * @return 在位置 u 出发,油量为 fuel 的前提下,到达 end 的「路径数量」
  24. */
  25. int dfs(int[] ls, int u, int end, int fuel) {
  26. // 如果缓存器中已经有答案,直接返回
  27. if (cache[u][fuel] != -1) {
  28. return cache[u][fuel];
  29. }
  30. int n = ls.length;
  31. // base case 1:如果油量为 0,且不在目标位置
  32. // 将结果 0 写入缓存器并返回
  33. if (fuel == 0 && u != end) {
  34. cache[u][fuel] = 0;
  35. return 0;
  36. }
  37. // base case 2:油量不为 0,且无法到达任何位置
  38. // 将结果 0 写入缓存器并返回
  39. boolean hasNext = false;
  40. for (int i = 0; i < n; i++) {
  41. if (i != u) {
  42. int need = Math.abs(ls[u] - ls[i]);
  43. if (fuel >= need) {
  44. hasNext = true;
  45. break;
  46. }
  47. }
  48. }
  49. if (fuel != 0 && !hasNext) {
  50. cache[u][fuel] = u == end ? 1 : 0;
  51. return cache[u][fuel];
  52. }
  53. // 计算油量为 fuel,从位置 u 到 end 的路径数量
  54. // 由于每个点都可以经过多次,如果 u = end,那么本身就算一条路径
  55. int sum = u == end ? 1 : 0;
  56. for (int i = 0; i < n; i++) {
  57. if (i != u) {
  58. int need = Math.abs(ls[i] - ls[u]);
  59. if (fuel >= need) {
  60. sum += dfs(ls, i, end, fuel - need);
  61. sum %= mod;
  62. }
  63. }
  64. }
  65. cache[u][fuel] = sum;
  66. return sum;
  67. }
  68. }

动态规划实现代码:

  1. class Solution {
  2. int mod = 1000000007;
  3. public int countRoutes(int[] ls, int start, int end, int fuel) {
  4. int n = ls.length;
  5. // f[i][j] 代表从位置 i 出发,当前油量为 j 时,到达目的地的路径数
  6. int[][] f = new int[n][fuel + 1];
  7. // 对于本身位置就在目的地的状态,路径数为 1
  8. for (int i = 0; i <= fuel; i++) f[end][i] = 1;
  9. // 从状态转移方程可以发现 f[i][fuel]=f[i][fuel]+f[k][fuel-need]
  10. // 在计算 f[i][fuel] 的时候依赖于 f[k][fuel-need]
  11. // 其中 i 和 k 并无严格的大小关系
  12. // 而 fuel 和 fuel-need 具有严格大小关系:fuel >= fuel-need
  13. // 因此需要先从小到大枚举油量
  14. for (int cur = 0; cur <= fuel; cur++) {
  15. for (int i = 0; i < n; i++) {
  16. for (int k = 0; k < n; k++) {
  17. if (i != k) {
  18. int need = Math.abs(ls[i] - ls[k]);
  19. if (cur >= need) {
  20. f[i][cur] += f[k][cur-need];
  21. f[i][cur] %= mod;
  22. }
  23. }
  24. }
  25. }
  26. }
  27. return f[start][fuel];
  28. }
  29. }