美文网首页
动态规划:198.打家劫舍, 337.打家劫舍III,279.完

动态规划:198.打家劫舍, 337.打家劫舍III,279.完

作者: zmfflying | 来源:发表于2021-03-02 17:55 被阅读0次

/**

198.打家劫舍

题目

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

0 <= nums.length <= 100
0 <= nums[i] <= 400

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

测试代码

print(rob([1,2,3,1]))

笔记

这道题就是很经典的动态规划的思路了
保证之前每一家都是当前可以得到的最大收益
然后计算出当前的最大收益

注意不能相邻不代表就是 +2 ,比如 [2,1,1,2] 最大是 2+2=4

代码地址

https://github.com/zmfflying/ZMathCode
*/

解题代码

import Foundation

//原始思路
func rob(_ nums: [Int]) -> Int {
    let count = nums.count

    //因为不能相邻 所以0和1提前处理掉
    if count == 0 {
        return 0
    }
    if count == 1 {
        return nums.last!
    }

    //记录每一家可以获得的最大收益
    var dp: [Int] = nums

    for i in 2..<count {
        var temp = 0
        var j = 2
        while i - j >= 0 {
            //计算每一家的最大收益需要和前面所有不相邻的都进行计算
            temp = max(temp, nums[i] + dp[i - j])
            j += 1
        }
        dp[i] = temp
    }
    //这样 最大值必定在最后两个之中
    return max(dp[count - 1], dp[count - 2])
}


//优化: 哪怕不抢当前房屋,也要保证每一家都是当前可以获得的最大收益
//比如 nums = [2,1], 那么dp[1] = 2
func rob1(_ nums: [Int]) -> Int {
    let count = nums.count
    
    //因为不能相邻 所以0和1提前处理掉
    if count == 0 {
        return 0
    }
    if count == 1 {
        return nums[count-1]
    }
    
    //记录每一家可以获得的最大收益
    var dp: [Int] = [Int].init(repeating: 0, count: count)
    dp[0] = nums[0]
    //保证每一家都是当前可以获得的最大收益
    //比如 nums = [2,1], 那么dp[1] = 2
    dp[1] = max(nums[0], nums[1])
    
    for i in 2..<count {
        dp[i] = max(dp[i-1], dp[i-2] + nums[i])
    }
    
    return dp[count-1]
}

/**

337.打家劫舍III

题目

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]

      3
     / \
    2   3
     \   \
      3   1

输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:

输入: [3,4,5,1,3,null,1]

      3
     / \
    4   5
   / \   \
  1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

测试代码

let tree5 = TreeNode.init(1, nil, nil)
let tree4 = TreeNode.init(3, nil, nil)
let tree3 = TreeNode.init(3, nil, tree5)
let tree2 = TreeNode.init(2, nil, tree4)
let tree1 = TreeNode.init(3, tree2, tree3)
print(rob(tree1))

笔记

这道题两个核心点:
第一,要使用后序遍历,这样根节点会最后一个被遍历的节点,根节点的最大值就是能盗取的最大金额

第二,这棵树,从下往上每个节点,都会有两种可能金额: 偷本次 和 不偷本次,其中
偷本节点的最大金额 = 本节点金额 + 不偷左子节点的最大金额 + 不偷右子节点的最大金额
不偷本节点的最大金额 = 偷左子节点的最大金额 + 偷右子节点的最大金额

用一个数组来记录并传递每个节点可收获的金额即可

代码地址

https://github.com/zmfflying/ZMathCode
*/

解题代码

import Foundation

func rob(_ root: TreeNode?) -> Int {
    
    func help(_ root: TreeNode?) -> [Int] {
        if root == nil {
            return [0,0]
        }
        let money = root!.val
        
        //计算出左右子节点可以获得的金额
        let left: [Int] = help(root?.left)
        let right: [Int] = help(root?.right)
        
        //不偷本节点的最大金额 = 左子节点的最大金额 + 右子节点的最大金额
        let noRobCurMoney = max(left[0], left[1]) + max(right[0], right[1])
        //偷本节点的最大金额 = 本节点金额 + 不偷左子节点的最大金额 + 不偷右子节点的最大金额
        let robCurMoney = money + left[0] + right[0]
        
        return [noRobCurMoney, robCurMoney]
    }
    
    let res = help(root)
    return max(res[0], res[1])
}

/**

279.完全平方数

题目

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:

输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

提示:

1 <= n <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/perfect-squares
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

测试代码

print(numSquares(12))

笔记

这道题其实也是动态规划的思路,从小到大
本次的最小完全平方数个数 等于 前面那个数的最小平方个数 加 1

代码地址

https://github.com/zmfflying/ZMathCode
*/

解题代码

import Foundation

// MARK: - 2.2 动态规划
func numSquares(_ n: Int) -> Int {
    if n == 1 { return 1 }

    var arr = Array(repeating: 0, count: n + 1)
    arr[1] = 1

    for i in 2...n {
        //记录本次 i 对应的值
        //temp 初始赋值为 i, 是因为 2 = 1 + 1, 3 = 1 + 1 + 1,,,,
        var temp = i
        
        //要把小于 1 到 i 之间的完全平方数都拿出来
        var j = 1
        while j * j <= i {
            
            //注意 arr[i - j*j] 是前面那个数 i - j*j 的最小完全平方数个数
            //所以要 arr[i - j*j] + 1 就是本次循环里 i 的最小完全平方数个数
            temp = min(temp, arr[i - j*j] + 1)
            j += 1
        }
        arr[i] = temp
    }
    return arr.last!
}


// MARK: - 1.1 递归
func numSquares1(_ n: Int) -> Int {
    var arr: [Int] = [Int].init(repeating: -1, count: n+1)

    var nums: [Int] = [Int]()
    for i in 1...n {
        let num = i * i
        if num > n {
            break
        }
        nums.append(num)
        arr[num] = 1
    }

    func help(_ n: Int) -> Int {
        if arr[n] != -1  {
            return arr[n]
        }

        var minLen = n
        for num in nums {
            if n < num {
                break
            }
            minLen = min(minLen, help(n - num) + 1)
        }
        arr[n] = minLen
        return minLen
    }

    return help(n)
}

相关文章

网友评论

      本文标题:动态规划:198.打家劫舍, 337.打家劫舍III,279.完

      本文链接:https://www.haomeiwen.com/subject/yesxqltx.html