打家劫舍,你会吗?| 刷题打卡

345 阅读2分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

image.png

image.png

今天给大家分享的题目是经典的动态规划题目之一,打家劫舍问题。

二、思路分析:

之前也做过一遍这道题目,今天再来做的时候又有不一样的思路。 今天做的时候我的思路是定义一个dp[i][j],j=0,1 表示前i个房子抢劫和不抢劫两种状态下得到的最大金额。

  • 那么状态转移方程就可以是下面两个,dp[i][0]表示第i个房子我不打劫时得到的最大金额,此时意味着我上一个房子i-1可以不打劫,或者我上一个房子i-1我打劫了,今天没法继续打劫了,取这两种情况中的大值。dp[i][1]表示第i个房子打劫时得到的最大值,既然打劫第i个房子,那么此时第i-1个房子是不能打劫的,所以此时的收益是不打劫第i-1个房子+打劫第i个房子带来的收益。所以我们可以很快写出代码1. dp[i][0] = max(dp[i - 1][0], dp[i - 1][1])

dp[i][1] = dp[i - 1][0] + nums[i]

  • 现在来看代码1,发现代码1里的状态只和上一行的状态有关,所以可以考虑滚动数组压缩,所以就有了代码2.代码2循环里记得先用临时变量保存下来上一行的dp0_0,因为他会被当前行的操作覆盖。

  • 今天看别人的题解的时候,发现状态方程还可以这样定义:定义dp[i]表示打劫前i间房子得到的最大收益。此时的状态转移方程如下:表示当打劫第i间房子时,收益和打劫前i-2房子的收益加当前房子收益,以及不打劫第i间房子时,收益和打劫前i-1间房子的收益的最大值。因此我们可以得到代码3.同样可以对代码3进行空间压缩,得到代码4。 dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

三、AC 代码:

//代码1
function rob1($nums) {
        $len_n = count($nums);
        if ($len_n == 0) {
            return 0;
        }
        if ($len_n <= 2) {
            return max($nums);
        }

        $dp = array_fill(0, $len_n, array_fill(0, 2, 0));
        $dp[0][0] = 0;
        $dp[0][1] = $nums[0];
        for ($i = 1; $i < $len_n; $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[$len_n - 1]);
    }
    
    //代码2
    function rob2($nums) {
        $len_n = count($nums);
        if ($len_n == 0) {
            return 0;
        }
        if ($len_n <= 2) {
            return max($nums);
        }

        $dp = array_fill(0, $len_n, array_fill(0, 2, 0));
        $dp0_0 = 0;
        $dp0_1 = $nums[0];
        for ($i = 1; $i < $len_n; $i++) {
            $t = $dp0_0;
            $dp0_0 = max($dp0_0, $dp0_1);
            $dp0_1 = $t + $nums[$i];
        }
        return max($dp0_0, $dp0_1);
    }
    
    //代码3
    function rob3($nums) {
        $len_n = count($nums);
        if ($len_n == 0) {
            return 0;
        }
        if ($len_n <= 2) {
            return max($nums);
        }

        //抢劫前N间房子得到的最大收益
        $dp = array_fill(0, $len_n, 0);
        $dp[0] = $nums[0];
        for ($i = 1; $i < $len_n; $i++) {
            $dp[$i] = max($dp[$i - 1], $dp[$i - 2] + $nums[$i]);
        }
        return $dp[$len_n - 1];
    }
    
    //代码4
    function rob($nums) {
        $len_n = count($nums);
        if ($len_n == 0) {
            return 0;
        }
        if ($len_n <= 2) {
            return max($nums);
        }

        //抢劫前N间房子得到的最大收益
        $dpi_2 = $nums[0];
        $dpi_1 = max($nums[1], $nums[0]);
        for ($i = 2; $i < $len_n; $i++) {
            $t = $dpi_1;
            $dpi_1 = max($dpi_1, $dpi_2 + $nums[$i]);
            $dpi_2 = $t;
        }
        return $dpi_1;
    }

四、总结:

动态规划的题目关键是状态的定义和状态转移方程的列出,只要你的状态合理,最后都能得到正确的答案。