代码随想录day41|198打家劫舍213打家劫舍II337打家劫舍III|01笔记

163 阅读2分钟
  • 198打家劫舍

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 使用动态规划的方法,dp[i]数组来表示到第i个家(从0开始)时能获得的最高金额。
  • 使用了dp[i] = max(dp[i-2]+nums[i],dp[i-1] )的转移公式
  • 遇到问题

image.png

  • 错误原因:直接赋值dp[1]的方式可能造成dp[1]位置的值并不符合dp数组的含义。
  • 改正办法:

image.png

  • 讲解观后感

  • 可以将dp数组设置为偷到第i个家(从1开始)时能获得的最高金额。就可以只初始化dp[1]了。
  • 解题代码

  • 从0开始
  •       func rob(nums []int) int {
              n := len(nums)
              if n<2 {
                  return nums[0]
              }
              if n==2 {
                  return max(nums[0], nums[1])
              }
              dp := make([]int, n)
              dp[0] = nums[0]
              dp[1] = max(nums[0], nums[1])
          
              for i:=2;i<n;i++ {
                  dp[i] = max(dp[i-2]+nums[i],dp[i-1] )// dp[i]表示偷到第i家(从0开始)能够偷得的最大金额
              }
          
              return dp[n-1]
          } 
          func max(x int, y int) int {
              if x>y {
                  return x
              } else {
                  return y
              }
          }
    
  • 从1开始
  •       func rob(nums []int) int {
              n := len(nums)
              dp := make([]int, n+1) // dp[i]表示偷到第i家(从1开始)能够偷得的最大金额
              dp[1] = nums[0]
              for i := 2; i <= n; i++ {
                  dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
              }
              return dp[n]
          }
          
          func max(a, b int) int {
              if a > b {
                  return a
              }
              return b
          }
    
  • 213打家劫舍II

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 总体方法还是动态规划,之后要加入初始位置和首尾关系的讨论。
  • 讲解观后感

  • 卡尔讨论了三种不同的首尾包含情况。但我在自己做题的过程中,直接只讨论分别不包括首尾的情况也成功ac了。
  • 解题代码

  •       func rob(nums []int) int {
              n := len(nums)
              if n<2 {
                  return nums[0]
              }
              dp := make([]int, n+1)
              dp[1] = nums[0]
              for i := 2; i < n; i++ {// dp[i]表示偷到第i家(从1开始)能够偷得的最大金额
                  dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
              }
              ans1 := dp[n-1]
          
              dp[1] = 0
              dp[2] = nums[1]
              for j:=3;j<n+1;j++ {
                  dp[j] = max(dp[j-1], dp[j-2] + nums[j-1])
              }
              ans2 := dp[n]
              
              return max(ans1, ans2)
          }
          
          func max(a, b int) int {
              if a > b {
                  return a
              }
              return b
          }
    
  • 337打家劫舍III

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 在树形结构中,警报的情况变成了在同一个通向叶子节点的路径中来判断。并且会造成父节点对兄弟节点的影响。不能简单的分层讨论。嘴和期望得到的数值要综合讨论所有分支的情况,那也许应该选择后序遍历。
  • 讲解观后感

  • 本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算
  • 我们要记录当前节点偷与不偷所得到的最大金钱。所以要用一个长度2的数组来记录
  • 遇到空节点直接返回[]int{0, 0}(后序遍历的终止条件,同时相当于dp数组的起始值)
  • 状态转移公式 (同时相当于遍历过程的处理)
  •       // 考虑去偷当前的屋子
          	robCur := cur.Val + left[0] + right[0]
              // 考虑不去偷当前的屋子
          	notRobCur := max(left[0], left[1]) + max(right[0], right[1])
    
  • 解题代码

  •     
        func rob(root *TreeNode) int {
        	res := robTree(root)
        	return max(res[0], res[1])
        }
        
        func robTree(cur *TreeNode) []int {
        	if cur == nil {
        		return []int{0, 0}
        	}
        	// 后序遍历
        	left := robTree(cur.Left)
        	right := robTree(cur.Right)
        
            // 考虑去偷当前的屋子
        	robCur := cur.Val + left[0] + right[0]
            // 考虑不去偷当前的屋子
        	notRobCur := max(left[0], left[1]) + max(right[0], right[1])
        
            // 注意顺序:0:不偷,1:去偷
        	return []int{notRobCur, robCur}
        }
        
        func max(a, b int) int {
        	if a > b {
        		return a
        	}
        	return b
        }