经典dp--抢家夺舍系列之Go写法
house-robber
首先我们从比较简单的角度来做:
- 判断状态:题目只有两种状态,也就是偷或者不偷,所以我们可以创建一个二维的数组,并用0表示不偷,1表示偷
- 数组的长度:这个实际上就是你房子的数量;
- 状态转移方程:当你在第i家时,选择不偷时,dp[i][0]就是选择dp[i-1]中最大的一个,选择偷的话,前面那家肯定不能偷,因此dp[i][1] = nums[i] + dp[i-1][0]。所以有状态转移方程:
- 最后Max(dp[len - 1][0], dp[len - 1][1])就是结果啦
// accept code 代码一
func rob(nums []int) int {
length := len(nums)
if length < 1 {
return 0
}
dp := make([][2]int, length)
dp[0][1] = nums[0]
dp[0][0] = 0
for i := 1; i < length; i++ {
dp[i][0] = max(dp[i-1][0], dp[i-1][1])
dp[i][1] = dp[i-1][0] + nums[i]
}
return max(dp[length-1][0], dp[length-1][1])
}
func max(values ...int) int {
maxValue := math.MinInt32
for _, value := range values {
if value > maxValue {
maxValue = value;
}
}
return maxValue;
}
值得注意的是,这个代码可以简化成一维数组,dp[i]表示当前i房子后能偷到的最多钱,所以我们可以利用i i-1 and i-2 来表示邻避关系,也就是说有状态转移方程:
//代码二
func rob(nums []int) int {
length := len(nums)
if length < 1 {
return 0
}
if length == 1 {
return nums[0];
}
dp := make([]int, length)
dp[0] = nums[0]
dp[1] = nums[1]
for i := 2; i < length; i++ {
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
}
return dp[length-1]
}
func max(values ...int) int {
maxValue := math.MinInt32
for _, value := range values {
if value > maxValue {
maxValue = value;
}
}
return maxValue;
}
通过代码二我们可以进一步优化空间复杂度,因为我们可见不论怎样,dp状态转移方程只跟
i i-1 and i-2 这三者有关,所以我们可以只使用三个变量dp, dp1, dp2来进行更新:
dp1 = dp2
dp2 = dp
因此有代码:
//代码三
func rob(nums []int) int {
length := len(nums)
if length < 1 {
return 0
}
dp1 := 0 //初始化为0 边界需要判断清晰
dp2 := 0
dp := 0
for i := 0; i < length; i++ {
dp = max(dp1 + nums[i], dp2)
dp1 = dp2
dp2 = dp
}
return dp
}
func max(values ...int) int {
maxValue := math.MinInt32
for _, value := range values {
if value > maxValue {
maxValue = value;
}
}
return maxValue;
}
house-robber-II
这个题目有限制就是数组是为一个环形数组,因此我们可以分为两种情况考虑,当选第一个来偷时,就不再考虑最后一个房子;当选最后一个来偷时,就不再考虑第一个房子;最后对于这两种情况选出一个最大值出来就好啦。
所以改改上面的代码就好了:
func rob(nums []int) int {
length := len(nums)
if length < 1 {
return 0;
} else if length == 1 {
return nums[0];
}
return max(subRob(nums,0, length - 2), subRob(nums, 1, length - 1))
}
func subRob(nums []int, st, ed int) int {
dp := 0
dp1 := 0
dp2 := 0
for i := st; i <= ed; i++ {
dp = max(dp1 + nums[i], dp2)
dp1 = dp2
dp2 = dp
}
return dp
}
func max(a, b int) int {
if a < b {
return b
}
return a
}
house-robber-III
这个是采用树的形式,同理,对于某一个节点我们可偷可不偷,我们存储两个状态,rob and noRob。 所以有计算公式:
func rob(root *TreeNode) int {
if root == nil {
return 0
}
noRob, rob := getResult(root);
return Max(noRob, rob);
}
func getResult(root *TreeNode) (a, b int) {
if root == nil{
return 0, 0
}
left0, left1 := getResult(root.Left)
right0, right1 := getResult(root.Right)
rob := root.Val + left0 + right0
// 可偷子节点或者不偷子节点,选最大的那个情况就好
noRob := Max(left0, left1) + Max(right0, right1)
return noRob, rob
}
/*
func getResult(root *TreeNode) (noRob, rob int) {
if root == nil{
return 0, 0
}
left0, left1 := getResult(root.Left)
right0, right1 := getResult(root.Right)
rob = root.Val + left0 + right0
noRob = Max(left0, left1) + Max(right0, right1)
return
}
*/
func Max(a,b int) int {
if a < b {
return b
} else {
return a
}
}