动态规划与贪心的区别:
动态规划当前的选择会对后续的选择产生影响
贪心只要不断选择当前的最优解即可
比如:
T198 打家劫舍
偷了1号,就不能偷2号
如果把“不能偷相邻的房间”条件去掉,就是一个贪心的问题。
总体思路:
所有的动态规划问题都可以通过递归的思路来解决,即问题分解为子问题
比如这道题,
每一次的选择都可以分解为:偷当前的房屋/不偷当前的房屋
假设现在面临选择的是n号房屋
①偷了当前的房屋,下一次的目标就只能是n+2号
②不偷当前的房屋,下一次的目标可以是n+1号
以此类推
只要选择两者中收益最大的情况即可
因此,递归语句的伪代码应该是
return max(当前房屋的价值+递归(n+2号房屋),递归(n+1号房屋))
由此得出初步的代码
//获取两个int数据中的最大值
int max(int a, int b){
return (a > b) ? a: b;
}
//递归函数
int solve(int index, int* nums, int numsSize){
if(index >= numsSize) return 0;
return max((nums[index] + solve(index + 2, nums, numsSize)), (nums[index] + solve(index + 3, nums, numsSize)));
}
int rob(int* nums, int numsSize) {
return max(solve(0, nums, numsSize), solve(1, nums, numsSize));
}
但是上述的代码时间复杂度过大,需要优化
优化思路:
考虑以下情况,
①不偷当前房屋,不偷n+1号房屋,偷n+2号房屋
即:递归情况为:solve(n)–>solve(n+1)–>solve(n+2)
②偷当前房屋,偷n+2号房屋
即:递归情况为:solve(n)–>solve(n+2)
这两种情况都需要对n+2号房屋进行递归,如果用一个数组记录对n+2号房屋决策的最大值,就避免了重复递归
即优化思路为用空间换时间
int max(int a, int b){
return (a > b) ? a: b;
}
int solve(int index, int* nums, int numsSize, int record[]){
//越界返回
if(index >= numsSize) return 0;
//如果已经决策过,直接返回决策结果
if(record[index] != -1) return record[index];
//记录子问题的最优解,避免重复计算
record[index] = max((nums[index] + solve(index + 2, nums, numsSize, record)), solve(index + 1, nums, numsSize, record));
return record[index];
}
int rob(int* nums, int numsSize) {
//记录每个房屋决策结果,初始化为-1
int record[numsSize];
for(int i = 0; i < numsSize; i++){
record[i] = -1;
}
return solve(0, nums, numsSize, record);
}
虽然这不是最优解,但是动态规划问题的一般思路。