[LeetCode] Coin Path 硬幣路徑


 

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() == -1) return {};
        int n = A.size();
        vector<int> res, dp(n, INT_MAX), pos(n, -1);
        dp[n - 1] = A[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            if (A[i] == -1) continue;
            for (int j = i + 1; j <= min(i + B, n - 1); ++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[0] == INT_MAX) return res;
        for (int cur = 0; cur != -1; cur = pos[cur]) {
            res.push_back(cur + 1);
        }
        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() == -1) return {};
        int n = A.size();
        vector<int> res, dp(n, INT_MAX), pos(n, -1), len(n, 0);
        dp[0] = 0;
        for (int i = 0; i < n; ++i) {
            if (A[i] == -1) continue;
            for (int j = max(0, 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] + 1)) {
                    dp[i] = t;
                    pos[i] = j;
                    len[i] = len[j] + 1;
                }
            }
        }
        if (dp[n - 1] == INT_MAX) return res;
        for (int cur = n - 1; cur != -1; cur = pos[cur]) {
            res.insert(res.begin(), cur + 1);
        }
        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 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM