zoukankan      html  css  js  c++  java
  • 记忆化搜索状态重叠

    在做 LeetCode 309. 最佳买卖股票时机含冷冻期 时,写完递归超时后,企图用记忆化搜索优化,但是发现原本正确的结果变成错的了

    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];
        }
    }
  • 相关阅读:
    .Net 开源项目资源大全
    无法向会话状态服务器发出会话状态请求
    网站限制不能点击右键
    ashx页面中context.Session["xxx"]获取不到值的解决办法
    SQL Server 2008 错误 233 的解决办法
    Best MVC Practices 最佳的MVC实践
    char* 转换成 CString
    MFC圆角背景移动边角底色毛刺解决方案
    CString转换为const char*
    分享一份安卓开发包,集成开发环境
  • 原文地址:https://www.cnblogs.com/GY8023/p/13579998.html
Copyright © 2011-2022 走看看