[LeetCode] Coin Path 硬币路径
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 any place (suppose the index is i
) in the array A
, you can jump to any one of the place in the array A
indexed i+1
, i+2
, …, i+B
if this place can be jumped to. Also, if you step on the index i
, you have to pay Ai coins. If Ai is -1, it means you can’t jump to the place indexed i
in the array.
Now, you start from the place indexed 1
in the array A
, and your aim is to reach the place indexed N
using the minimum coins. You need to return the path of indexes (starting from 1 to N) in the array you should take to get to the place indexed N
using minimum coins.
If there are multiple paths with the same cost, return the lexicographically smallest such path.
If it's not possible to reach the place indexed N then you need to return an empty array.
Example 1:
Input: [1,2,4,-1,2], 2
Output: [1,3,5]
Example 2:
Input: [1,2,4,-1,2], 1
Output: []
Note:
- Path Pa1, Pa2, ..., Pan is lexicographically smaller than Pb1, Pb2, ..., Pbm, if and only if at the first
i
where Pai and Pbi differ, Pai < Pbi; when no suchi
exists, thenn
<m
. - A1 >= 0. A2, ..., AN (if exist) will in the range of [-1, 100].
- Length of A is in the range of [1, 1000].
- B is in the range of [1, 100].
这道题给了我们一个数组A,又给了我们一个整数B,表示能走的最大步数,数组上的每个数字都是cost值,如果到达某个位置,就要加上该位置上的数字,其实位置是在第一个数字上,目标是到达末尾位置,我们需要让总cost值最小,并输入路径,如果cos相同的话,输出字母顺序小的那个路径。还有就是如果数组上的某个位置为-1的话,表示到达该位置后不能再去下一个位置,而且数组末位置不能为-1。博主最开始写了一个递归的解法,结果MLE了,看来这道题对内存使用的管控极为苛刻。所以我们不能将所有的候选路径都存在内存中,而是应该建立祖先数组,即数组上每个位置放其父结点的位置,有点像联合查找Union Find中的root数组,再最后根据这个祖先数组来找出正确的路径。由于需要找出cost最小的路径,所以我们可以考虑用dp数组,其中dp[i]表示从开头到位置i的最小cost值,但是如果我们从后往前跳,那么dp[i]就是从末尾到位置i的最小cost值。
我们首先判断数组A的末尾数字是否为-1,是的话直接返回空集。否则就新建结果res数组,dp数组,和pos数组,其中dp数组都初始化为整型最大值,pos数组都初始化为-1。然后将dp数组的最后一个数字赋值为数组A的尾元素。因为我们要从后往前跳,那我们从后往前遍历,如果遇到数字-1,说明不能往前跳了,直接continue继续循环,然后对于每个遍历到的数字,我们都要遍历其上一步可能的位置的dp[j]值来更新当前dp[i]值,由于限制了步数B,所以最多能到i+B,为了防止越界,要取i+B和n-1中的较小值为界限,如果上一步dp[j]值为INT_MAX,说明上一个位置无法跳过来,直接continue,否则看上一个位置dp[j]值加上当前cost值A[i],如果小于dp[i],说明dp[i]需要更新,并且建立祖先数组的映射pos[i] = j。最后在循环结束后,我们判断dp[0]的值,如果是INT_MAX,说明没有跳到首位置,直接返回空集,否则我们就通过pos数组来取路径。我们从前往后遍历pos数组来取位置,直到遇到-1停止。另外要说明的就是,这种从后往前遍历的模式得到的路径一定是字母顺序最小的,zestypanda大神的帖子中有证明,不过博主没太看懂-.-|||,可以带这个例子尝试:
A = [0, 0, 0], B = 2
上面这个例子得到的结果是[1, 2, 3],是字母顺序最小的路径,而相同的cost路径[1, 3],就不是字母顺序最小的路径,参见代码如下:
解法一:
class Solution {
public:
vector<int> cheapestJump(vector<int>& A, int B) {
if (A.back() == -) return {};
int n = A.size();
vector<int> res, dp(n, INT_MAX), pos(n, -);
dp[n - ] = A[n - ];
for (int i = n - ; i >= ; --i) {
if (A[i] == -) continue;
for (int j = i + ; j <= min(i + B, n - ); ++j) {
if (dp[j] == INT_MAX) continue;
if (A[i] + dp[j] < dp[i]) {
dp[i] = A[i] + dp[j];
pos[i] = j;
}
}
}
if (dp[] == INT_MAX) return res;
for (int cur = ; cur != -; cur = pos[cur]) {
res.push_back(cur + );
}
return res;
}
};
下面这种方法是正向遍历的解法,正向跳的话就需要另一个数组len,len[i]表示从开头到达位置i的路径的长度,如果两个路径的cost相同,那么一定是路径长度大的字母顺序小,可以参见例子 A = [0, 0, 0], B = 2。
具体的写法就不讲了,跟上面十分类似,参考上面的讲解,需要注意的就是更新的判定条件中多了一个t == dp[i] && len[i] < len[j] + 1,就是判断当cost相同时,我们取长度大路径当作结果保存。还有就是最后查找路径时要从末尾往前遍历,只要遇到-1时停止,参见代码如下:
解法二:
class Solution {
public:
vector<int> cheapestJump(vector<int>& A, int B) {
if (A.back() == -) return {};
int n = A.size();
vector<int> res, dp(n, INT_MAX), pos(n, -), len(n, );
dp[] = ;
for (int i = ; i < n; ++i) {
if (A[i] == -) continue;
for (int j = max(, i - B); j < i; ++j) {
if (dp[j] == INT_MAX) continue;
int t = A[i] + dp[j];
if (t < dp[i] || (t == dp[i] && len[i] < len[j] + )) {
dp[i] = t;
pos[i] = j;
len[i] = len[j] + ;
}
}
}
if (dp[n - ] == INT_MAX) return res;
for (int cur = n - ; cur != -; cur = pos[cur]) {
res.insert(res.begin(), cur + );
}
return res;
}
};
类似题目:
参考资料:
https://discuss.leetcode.com/topic/98399/c-dp-o-nb-time-o-n-space
https://discuss.leetcode.com/topic/98491/java-22-lines-solution-with-proof
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Coin Path 硬币路径的更多相关文章
- [LeetCode] 656. Coin Path 硬币路径
Given an array A (index starts at 1) consisting of N integers: A1, A2, ..., AN and an integer B. The ...
- [LeetCode] 112. Path Sum 路径和
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...
- [LeetCode] Coin Change 硬币找零
You are given coins of different denominations and a total amount of money amount. Write a function ...
- [LeetCode] Simplify Path 简化路径
Given an absolute path for a file (Unix-style), simplify it. For example,path = "/home/", ...
- [leetcode]112. Path Sum路径和(是否有路径)
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...
- 【LeetCode】Path Sum(路径总和)
这道题是LeetCode里的第112道题.是我在学数据结构——二叉树的时候碰见的题.题目要求: 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和 ...
- LeetCode 112. Path Sum路径总和 (C++)
题目: Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up ...
- [LeetCode] 113. Path Sum II 路径和 II
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given su ...
- [LeetCode] 437. Path Sum III 路径和 III
You are given a binary tree in which each node contains an integer value. Find the number of paths t ...
随机推荐
- java设计模式-State(状态)模式
state定义 不同的状态,不同的行为;或者说,每个状态有着相应的行为. 就像电风扇的开关,一档的上一个是关闭,关闭的上一个是五档,五档的上一个是四档,以此类推,而且五档的下一 ...
- Jmeter-基于Ubuntu运行
这几天折腾了很久,整合了一套接口自动化的持续集成工具,先从最基础的运行Jmeter说起.由于我是用Docker部署的持续集成环境,所以接口运行必须在服务器上 一:在Linux服务器先安装jdk 1:先 ...
- CountDownLatch 源码解析—— await()
上一篇文章说了一下CountDownLatch的使用方法.这篇文章就从源码层面说一下await() 的原理. 我们已经知道await 能够让当前线程处于阻塞状态,直到锁存器计数为零(或者线程中断). ...
- Python基本数据结构--列表
列表: 1.有序的集合: 2.通过偏移来索引,从而读取数据: 3.支持嵌套: 4.可变的类型: 列表的操作: 1.切片: a = [1,2,3,4,5,6,7] 正向索引 反向索引 默认索引 2.添加 ...
- 201621123043 《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 泛型的定义: 泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此 ...
- segmentedControl设置字体和字体颜色问题
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor blackColor],UITextAttributeT ...
- JAVA中if多分支和switch的优劣性。
Switch多分支语句switch语句是多分支选择语句.常用来根据表达式的值选择要执行的语句.例如,在某程序中,要求将输入的或是获取的用0-6代表的星期,转换为用中文表示的星期.该需求通过伪代码描述的 ...
- mycat入门_介绍与安装
利用闲暇时间接触了下mycat. 一.介绍 1.概述: 国内最活跃的.性能最好的开源数据库中间件,可以理解为数据库和应用层之间的一个代理组件. 2.作用: 读写分离.分表分库.主从切换. 3.原理: ...
- DBA 小记 — 分库分表、主从、读写分离
前言 我在上篇博客 "Spring Boot 的实践与思考" 中比对不同规范的 ORM 框架应用场景的时候提到过主从与读写分离,本篇随笔将针对此和分库分表进行更深入地探讨. 1. ...
- 微信公众号Markdown编辑器, 适合代码排版
随着大家都转战微信公众平台,如何快速的编写文章就摆在了首要位置.不可否认,使用微信自带的编辑器可以做出好看的排版,甚至用第三方编辑器有更多的模板.但是,这些全部都需要手动的调整.本来公众平台就算是自媒 ...