金币路径
难度:
标签:
题目描述
代码结果
运行时间: 66 ms, 内存: 16.2 MB
/*
* LeetCode 656: Coin Path (Using Java Streams)
* Given an array A (index starts at 1) consisting of N integers: A1, A2, ..., AN and an integer B.
* The integer B denotes that from Ai, you can jump to Ai+1, Ai+2, ..., Ai+B if 1 <= i + j <= N.
* Find the minimum cost path to reach AN from A1, and output the indices of this path.
* If there are multiple paths with the same cost, return the lexicographically smallest such path.
* If no such path exists, return an empty list.
*/
import java.util.*;
import java.util.stream.*;
public class CoinPathStream {
public List<Integer> cheapestJump(int[] A, int B) {
int N = A.length;
int[] cost = new int[N];
int[] path = new int[N];
Arrays.fill(cost, Integer.MAX_VALUE);
cost[0] = 0;
List<Integer>[] res = new List[N];
for (int i = 0; i < N; i++) res[i] = new ArrayList<>();
res[0].add(1);
IntStream.range(0, N).forEach(i -> {
if (A[i] == -1) return;
IntStream.range(i + 1, Math.min(i + B + 1, N)).forEach(j -> {
if (A[j] == -1) return;
int newCost = cost[i] + A[j];
if (newCost < cost[j]) {
cost[j] = newCost;
path[j] = i;
res[j] = new ArrayList<>(res[i]);
res[j].add(j + 1);
} else if (newCost == cost[j]) {
List<Integer> newPath = new ArrayList<>(res[i]);
newPath.add(j + 1);
if (comparePaths(newPath, res[j]) < 0) {
res[j] = newPath;
}
}
});
});
return res[N - 1];
}
private int comparePaths(List<Integer> path1, List<Integer> path2) {
for (int i = 0; i < Math.min(path1.size(), path2.size()); i++) {
if (!path1.get(i).equals(path2.get(i))) {
return path1.get(i) - path2.get(i);
}
}
return path1.size() - path2.size();
}
}
解释
方法:
这个题解使用了动态规划的思路。从终点开始倒序遍历数组,对于每个位置,计算从该位置跳到终点的最小花费。状态转移方程为:dp[i] = min(dp[j] + coins[i]),其中 i + 1 <= j <= min(n, i + maxJump + 1)。同时使用 nxt 数组记录每个位置的最优下一跳位置,用于最后重构最小花费路径。
时间复杂度:
O(n * maxJump),最坏情况下为 O(n^2)
空间复杂度:
O(n)
代码细节讲解
🦆
在动态规划中,为什么选择从终点开始倒序遍历数组,而不是从起点开始正序遍历?
▷🦆
在处理`coins[i] == -1`的情况时,直接跳过该位置是否会影响到最终的路径重构?
▷🦆
状态转移方程`dp[i] = min(dp[j] + coins[i])`里为什么是加上`coins[i]`而不是`coins[j]`?
▷🦆
当`maxJump`的值非常大时,存在优化内层循环的算法吗?
▷相关问题
打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2] 输出:3 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1] 输出:4 解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3] 输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000