記憶化搜尋狀態重疊
阿新 • • 發佈:2020-08-29
在做 LeetCode309. 最佳買賣股票時機含冷凍期時,寫完遞迴超時後,企圖用記憶化搜尋優化,但是發現原本正確的結果變成錯的了
Debug過程:
對於 [2,1,4] 這個用例,列出所有情況後發現最後一步的 not sell 和 buy 兩個狀態重疊了,導致記憶化錯誤
隨後改為自頂向下的dp通過
查閱資料發現:https://www.cnblogs.com/zcy0917/p/9316487.html
記憶化搜尋的適用範圍
根據記憶化搜尋的思想,它是解決重複計算,而不是重複生成,也就是說,這些搜尋必須是在搜尋擴充套件路徑的過程中分步計算的題目,也就是“搜尋答案與路徑相關”的題目,而不能是搜尋一個路徑之後才能進行計算的題目,必須要分步計算,並且搜尋過程中,一個搜尋結果必須可以建立在同類型問題的結果上,也就是類似於動態規劃解決的那種。
也就是說,他的問題表達,不是單純生成一個走步方案,而是生成一個走步方案的代價等,而且每走一步,在搜尋樹/圖中生成一個新狀態,都可以精確計算出到此為止的費用,也就是,可以分步計算,這樣才可以套用已經得到的答案.
使用條件
- 對於一定狀態有唯一相同的解,不應對於一個狀態有多個解(解不相同);
- 到達底層時可立即返回解,不應得出路徑後才能計算解;
- 狀態數量和規模應能夠在有限資料結構中儲存。
TIP
- 遵循無後效性原則;//某階段的狀態一旦確定,則此後過程的演變不再受此前各種狀態及決策的影響。
- 侷限性:可十分簡潔的優化為遞迴,即動態規劃。
- 往往對於dfs才會使用記憶化,因為bfs並不會重複搜尋到某一個狀態,而一旦搜尋到結果就是最優解,此時立即退出;
遞迴:
class Solution { int[] prices; public int iter(int idx, boolean have, boolean frozen, int profit){ if (idx == prices.length - 1){ if (have){ profit += prices[prices.length - 1]; } return profit; } if (frozen){return iter(idx + 1, have, false, profit); } if (have){ int sell = iter(idx+1, false, true, profit + prices[idx]); int notsell = iter(idx+1, true, false, profit); return Math.max(sell, notsell); }else { int buy = iter(idx+1, true, false, profit - prices[idx]); int notbuy = iter(idx+1, false, false, profit); return Math.max(buy, notbuy); } } public int maxProfit(int[] prices){ if (prices == null || prices.length <=0){ return 0; } this.prices = prices; return iter(0, false, false, 0); } }
記憶化搜尋:
public class Solution { int[] prices; Integer[][][] dp; int len; public int iter(int idx, int have, int frozen, int profit){ if (dp[idx][have][frozen] != null){ return dp[idx][have][frozen]; } if (idx == prices.length - 1){ if (have == 1){ profit += prices[prices.length - 1]; } dp[idx][have][frozen] = profit; return profit; } int res; if (frozen == 1){ res = iter(idx + 1, 0, 0, profit); dp[idx][have][frozen] = res; return res; } if (have == 1){ int sell = iter(idx+1, 0, 1, profit + prices[idx]); int notsell = iter(idx+1, 1, 0, profit); res = Math.max(sell, notsell); }else { int buy = iter(idx+1, 1, 0, profit - prices[idx]); int notbuy = iter(idx+1, 0, 0, profit); res = Math.max(buy, notbuy); } dp[idx][have][frozen] = res; return res; } public int maxProfit(int[] prices){ if (prices == null || prices.length <=0){ return 0; } this.prices = prices; this.len = prices.length; dp = new Integer[len][2][2]; return iter(0, 0, 0, 0); } }
dp:
class Solution { public int maxProfit(int[] prices) { if (prices == null || prices.length <=0){ return 0; } int len = prices.length; // 這天開始的時候 idx, 有無股票, 是否在冷凍期 Integer[][][] dp = new Integer[len][2][2]; dp[len-1][0][0] = 0; dp[len-1][1][0] = prices[len-1]; dp[len-1][0][1] = 0; // 不可能出現dp[i][1][1] for (int i = len - 2; i >= 0; --i) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { if (k == 1){ if (j != 1){ // i 0 1 dp[i][j][k] = dp[i+1][0][0]; } } else { if (j == 1){ // sell / not sell dp[i][j][k] = Math.max(dp[i+1][0][1] + prices[i], dp[i+1][1][0]); }else { // buy / not buy dp[i][j][k] = Math.max(dp[i+1][1][0] - prices[i], dp[i+1][0][0]); } } } } } return dp[0][0][0]; } }