你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
解题思路:
定义了一个宏 MAX ,用于返回两个数 a 和 b 中的较大值。在后续代码中会用它来比较不同选择下的最大抢劫金额。
深度优先搜索函数 dfs :
递归边界:当 i < 0 时,说明已经没有房子可供考虑了,此时能抢劫到的金额为 0 ,所以返回 0 。
记忆化检查: memo 数组用于记录已经计算过的位置 i 能抢劫到的最大金额。如果 memo[i] != 1 ,说明该位置已经计算过,直接返回 memo[i] ,避免重复计算,提高效率。
两种选择:
not_choose = dfs(i - 1, nums, memo); 表示不抢劫当前房子(位置 i ),那么此时能抢劫到的最大金额就是从下一个房子(位置 i - 1 )开始继续搜索能得到的最大金额,通过递归调用 dfs 函数实现。
choose = dfs(i - 2, nums, memo) + nums[i]; 表示抢劫当前房子(位置 i ),由于不能抢劫相邻的房子,所以此时能抢劫到的最大金额是从再下一个房子(位置 i - 2 )开始继续搜索能得到的最大金额加上当前房子的金额,同样通过递归调用 dfs 函数实现。
返回结果:比较 not_choose 和 choose ,取较大值存入 memo[i] 并返回,即选择两种情况中能获得更大金额的方式。
主函数 rob :
初始化记忆化数组:使用 malloc 动态分配一个大小为 numsSize 的数组 memo ,并使用 memset 将其所有元素初始化为 -1 ,表示这些位置的最大抢劫金额还未计算过。
调用深度优先搜索:从最后一个房子(位置 numsSize - 1 )开始调用 dfs 函数进行深度优先搜索,计算能抢劫到的最大金额,并将结果存储在 ans 中。
释放内存:使用 free 释放动态分配的 memo 数组内存,避免内存泄漏。
返回结果:返回计算得到的最大抢劫金额 ans
// 定义一个宏,用于返回两个数中的较大值,((b) > (a) ? (b) : (a)) 是一个三目运算符,当 b 大于 a 时返回 b,否则返回 a
#define MAX(a, b) ((b) > (a) ? (b) : (a))
// 深度优先搜索函数,用于计算从位置 i 开始能抢劫到的最大金额
// i 表示当前考虑的房子位置,nums 是存储每个房子金额的数组,memo 是用于记忆化搜索的数组
int dfs(int i, int* nums, int* memo) {
if (i < 0) {
return 0; // 当位置小于 0 时,说明没有房子可抢劫了,返回 0 作为抢劫金额
}
if (memo[i] != -1) {
return memo[i]; // 如果 memo 数组中该位置的值不为 -1,说明之前已经计算过从该位置开始的最大抢劫金额,直接返回该值
}
int not_choose = dfs(i - 1, nums, memo);
// 计算不抢劫当前房子(位置 i)时,从下一个房子(位置 i - 1)开始能抢劫到的最大金额,通过递归调用 dfs 函数
int choose = dfs(i - 2, nums, memo) + nums[i];
// 计算抢劫当前房子(位置 i)时,从再下一个房子(位置 i - 2)开始能抢劫到的最大金额加上当前房子的金额,通过递归调用 dfs 函数
return memo[i] = MAX(not_choose, choose);
// 取不抢劫当前房子和抢劫当前房子两种情况中的较大值,存入 memo 数组中,并返回该值
}
// 主函数,用于计算在给定房子金额数组下能抢劫到的最大金额
// nums 是存储每个房子金额的数组,numsSize 是房子的数量
int rob(int* nums, int numsSize) {
int* memo = malloc(numsSize * sizeof(int));
// 动态分配一个大小为 numsSize 的整型数组,用于存储每个位置开始的最大抢劫金额,以便后续记忆化搜索使用
memset(memo, -1, numsSize * sizeof(int));
// 将 memo 数组中的每个元素初始化为 -1,表示还未计算过该位置开始的最大抢劫金额
int ans = dfs(numsSize - 1, nums, memo);
// 从最后一个房子(位置 numsSize - 1)开始调用 dfs 函数进行计算,得到最终能抢劫到的最大金额
free(memo);
// 释放动态分配的 memo 数组内存,避免内存泄漏
return ans;
// 返回能抢劫到的最大金额
}
实验小结:本次基于DFS和记忆化解决小偷抢劫问题的实现,达成了在不触动警报前提下算出最高偷窃金额的目标。代码通过宏定义快速比较数值大小,利用 dfs 函数递归探索抢劫或不抢劫当前房屋的收益,并借助记忆化数组 memo 避免重复计算,提升效率。 rob 函数负责初始化 memo ,从最后一间房屋开始调用 dfs 求解。