#动态规划
思路分析
我们可以将选的过程画成一颗二叉树,如下,其中0-4表示的是 i 的值,选与不选表示的是是否选择4这个节点
代码如下
javaimport java.util.*;
class Solution {
private int[] nums;
public int rob(int[] nums) {
this.nums = nums;
int n = nums.length;
return dfs(n - 1);
}
/**
* 递归遍历房子
*
* @param i
* @return
*/
private int dfs(int i) {
// 如果i<0表示没有房子可选了,直接返回0
if (i < 0) {
return 0;
}
// 使用状态转移方程表示获取的最大值
/*
* dfs(i-1)表示在当前位置i不选,因为 “如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。”
* dfs(i - 2) + nums[i] 表示在当前位置i选
* 使用max则可选出这两种操作哪种得到的值最大
*/
int res = Math.max(dfs(i - 1), dfs(i - 2) + this.nums[i]);
return res;
}
}
显然,答案超时了,我们需要优化代码,我们注意到搜索树,它有一些重复的地方,我们可以进行剪枝操作
优化一
javaimport java.util.*;
class Solution {
private int[] nums;
private int[] memo;
public int rob(int[] nums) {
this.nums = nums;
int n = nums.length;
this.memo = new int[n];
Arrays.fill(this.memo, -1);
return dfs(n - 1);
}
/**
* 递归遍历房子
*
* @param i
* @return
*/
private int dfs(int i) {
// 如果i<0表示没有房子可选了,直接返回0
if (i < 0) {
return 0;
}
// 如果当前值计算过了,那么直接返回
if (this.memo[i] != -1) {
return this.memo[i];
}
// 使用状态转移方程表示获取的最大值
/*
* dfs(i-1)表示在当前位置i不选,因为 “如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。”
* dfs(i - 2) + nums[i] 表示在当前位置i选
* 使用max则可选出这两种操作哪种得到的值最大
*/
int res = Math.max(dfs(i - 1), dfs(i - 2) + this.nums[i]);
this.memo[i] = res;
return res;
}
}
这样算法的复杂度就优化到了
优化二
代码如下
javaimport java.util.*;
class Solution {
public int rob(int[] nums) {
int n = nums.length;
int[] f = new int[n + 2];
for (int i = 0; i < n; i++) {
f[i + 2] = Math.max(f[i + 1], f[i] + nums[i]);
}
return f[n - 1 + 2];
}
}
此时的时间复杂度是 ,空间复杂度也是
优化三
计算完后更新 和 即可
代码如下
javaimport java.util.*;
class Solution {
public int rob(int[] nums) {
int n = nums.length;
int f0 = 0, f1 = 0, newF = 0;
for (int i = 0; i < n; i++) {
newF = Math.max(f1, f0 + nums[i]);
f0 = f1;
f1 = newF;
}
return newF;
}
}
这时的事件复杂度是 ,空间复杂度就是 了
#动态规划 #0-1背包问题
得到 0-1背包
代码
javaclass Solution {
private int[] w;
private int[] v;
private int[] memo;
/**
* 背包问题
*
* @param capacity 背包容量
* @param w n个物品的体积
* @param v n个物品的价值
* @return 所选物品在不超过capacity的情况下的最大价值
*/
private int zeroOneKnapsack(int capacity, int[] w, int[] v) {
int n = w.length;
this.w = w;
this.v = v;
this.memo = new int[n];
Arrays.fill(this.memo, -1);
return dfs(n - 1, capacity);
}
/**
* 遍历
*
* @param i 第i个元素
* @param c 背包容量
* @return
*/
private int dfs(int i, int c) {
if (i < 0) {
return 0;
}
if (this.memo[i] != -1) {
return this.memo[i];
}
if (c < this.w[i]) {
return dfs(i - 1, c);
}
// 选(dfs(i - 1, c - this.w[i]) + this.v[i])或者不选(dfs(i - 1, c))
int res = Math.max(dfs(i - 1, c), dfs(i - 1, c - this.w[i]) + this.v[i]);
this.memo[i] = res;
return res;
}
}
目标和思路分析
javaimport java.util.*;
class Solution {
private int[] nums;
private int[][] memo;
public int findTargetSumWays(int[] nums, int target) {
// p: nums中的+数
// ng: s-p nums中的-数 注意,这里不是负数,而是不带-的数的和
// 可得 p-(s-p)=target
// 2p=target+s
// p=(target+s)/2 我们需要求p,计算哪些条件下p是可以满足的
// (target+s)为偶数 且不为负数(为什么要判断是否为负数,因为target可能为负数,而sum+target可能小于0,则不可能取得结果)
this.nums = nums;
target += Arrays.stream(nums).sum();
if (target < 0 || (target % 2 == 1)) {
return 0;
}
// 除以2去计算p
target /= 2;
// 01背包
int n = nums.length;
this.memo = new int[n][target + 1];
for (int i = 0; i < n; i++) {
Arrays.fill(this.memo[i], -1);
}
return dfs(n - 1, target);
}
/**
* 遍历
*
* @param i 第i个元素
* @param c 背包容量
* @return
*/
private int dfs(int i, int c) {
if (i < 0) {
// c == 0表示这些数加起来等于target
if (c == 0) {
return 1; // 表示这组合可以 返回1
}
// c != 0 表示这些数加起来不等于target
if (c != 0) {
return 0; // 表示这组合不行 返回0
}
}
if (this.memo[i][c] != -1) {
return this.memo[i][c];
}
if (c < this.nums[i]) {
return dfs(i - 1, c);
}
// 这里计算了
int res = dfs(i - 1, c) + dfs(i - 1, c - this.nums[i]);
this.memo[i][c] = res;
return res;
}
}
本文作者:peepdd864
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!