本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
今天给大家分享的题目是经典的动态规划题目之一,打家劫舍问题。
二、思路分析:
之前也做过一遍这道题目,今天再来做的时候又有不一样的思路。 今天做的时候我的思路是定义一个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;
}
四、总结:
动态规划的题目关键是状态的定义和状态转移方程的列出,只要你的状态合理,最后都能得到正确的答案。