Given an array A (index starts at 1) consisting of N integers: A1, A2, ..., AN and an integer B. The integer Bdenotes 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+1i+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:

  1. 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 such i exists, then n < m.
  2. A1 >= 0. A2, ..., AN (if exist) will in the range of [-1, 100].
  3. Length of A is in the range of [1, 1000].
  4. 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;
}
};

类似题目:

House Robber II

House Robber

Frog Jump

Jump Game

Jump Game II

参考资料:

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 硬币路径的更多相关文章

  1. [LeetCode] 656. Coin Path 硬币路径

    Given an array A (index starts at 1) consisting of N integers: A1, A2, ..., AN and an integer B. The ...

  2. [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 ...

  3. [LeetCode] Coin Change 硬币找零

    You are given coins of different denominations and a total amount of money amount. Write a function ...

  4. [LeetCode] Simplify Path 简化路径

    Given an absolute path for a file (Unix-style), simplify it. For example,path = "/home/", ...

  5. [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 ...

  6. 【LeetCode】Path Sum(路径总和)

    这道题是LeetCode里的第112道题.是我在学数据结构——二叉树的时候碰见的题.题目要求: 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和 ...

  7. 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 ...

  8. [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 ...

  9. [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 ...

随机推荐

  1. 查看Linux内核版本之lsb_release

    lsb_release命令LSB是Linux Standard Base的缩写,lsb_release命令用来显示LSB和特定版本的相关信息,可通过yum -y install redhat-lsb命 ...

  2. lua精灵移除报对象非法

    function addLeftCard(isVisible) if self.left_CardSprite == nil then self.left_CardSprite = cc.Sprite ...

  3. .Net的垃圾回收机制(GC)之拙见——托管类型的垃圾回收

    各种语言的垃圾回收在IT界噪的沸沸扬扬,有极大的优化同时也有瓶颈. 而在.Net中的垃圾回收机制又是怎样的呢? 众所知周,.Net中的垃圾回收机制是由.Net Framework托管的,带给开发者最大 ...

  4. ibatis.net 入门demo 实现基本增删改查

    1.项目架构体系  DAO(数据访问层)   Domain(实体层)  Text(表示层) 2.比较重要的是需要添加两个dll的引用,以及两个配置文件和一个XML文件 两个 IbatisNet.Com ...

  5. 团队作业9——事后分析(Beta版本)

    事后诸葛亮分析 1.         总结 团队合照   a. 项目管理之事后诸葛亮会 ·设想和目标 (1)我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 个人学习 ...

  6. Alpha冲刺第一天

    Alpha冲刺第一天 站立式会议 项目进展 项目的第一天,主要工作是对项目的开发进行规划,以及将规划的成果转化为燃尽图与博客文章.依据项目需求分析报告与开题报告中已经完成的设计任务和项目规划,我们将系 ...

  7. iOS开发-继承特征详解

    面向对象的三大特性:封装,继承,多态. 1.继承 继承既能保证类的完整,又能简化代码. 把公共的方法和实例变量写在子类,子类只需要写父类独有的实例变量和方法即可. 继承是面向对象三大特性之一,合理的继 ...

  8. Flask 扩展 Flask-RESTful

    Flask路由可以指定HTTP请求方法,并在请求函数中根据不同的请求方法,执行不同的逻辑.这样实现一个Restful的请求已经相当简单了 Flask还有更简便的方法,就是其Flask-RESTful扩 ...

  9. java截取一个字符串正数或倒数某个特定字符前后的内容

    取出正数第二个“.”后面的内容 public class TestCode { public static void main(String[] args) { String str ="2 ...

  10. 深入了解GOT,PLT和动态链接

    之前几篇介绍exploit的文章, 有提到return-to-plt的技术. 当时只简单介绍了 GOT和PLT表的基本作用和他们之间的关系, 所以今天就来详细分析下其具体的工作过程. 本文所用的依然是 ...