Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
初试
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ho… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
复试
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ho… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
HR面试
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ho… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、思路分析:
初试
每一个房间无疑就两种选择,或者说是状态(偷/不偷),而偷还是不偷又取决于前一个偷还是不偷,我们要保证的是最终的金额数最大,那么显然后面的状态要依赖与前面的状态,这时候我们就要考虑下动态规划了,那么怎么初试化呢?对于第一个房间(只有一个房间)当然是偷了比较多钱呀,对于前两个房间(有两个房间),那当然是偷钱多的那个房间啊,对于有3个和三个以上呢?那就考虑当前的房间是偷还是不偷了,这就是递推公式
dp[0]=nums[0];//一个房间
dp[1]=Math.max(nums[0],nums[1]);//两个房间
dp[n]=Math.max(nums[n]+dp[n-2],dp[n-1]);//n>=3,三个及以上的房间
复试
咋一看和初试的差不多,就是成环了,我第一眼看到就是想着把它当成循环数组来做,取余取余,后来仔细想想好像没那么麻烦,于上一题不同的地方无疑就是在第一个房间和最后一个房间,其他都是一样的,状态都一样,那么我就特殊考虑这两个房间就好了,假设偷第一间房间,那么最后一间就不偷,假设偷第一间房间,那么最后一间房间也不一定偷,注意这里的不一定,因为你要考虑最后一间的前面一间偷还是不偷,才能决定最后一间的状态。
//第一种情况----第一间偷
dp[0]=nums[0];
dp[1]=nums[0];
dp[n]=Math.max(nums[n]+dp[n-2],dp[n-1]);//n>=3,三个及以上的房间;注意这里的n不用到最后一间了,因为最后一间是一定不能偷的了。
//第二种情况----第一间不偷
dp[0]=0;
dp[1]=nums[1];
dp[n]=Math.max(nums[n]+dp[n-2],dp[n-1]);//n>=3,三个及以上的房间;注意这里的n必须到最后一间了,因为最后一间是偷还是不偷取决于你的上一间
HR面试
这题变化就大了,小偷都会数据结构了!但是每个房间无疑还是那两种状态偷与不偷,那直接便利整个树暴力破解
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var rob = function(root) {
if(!root) return 0;
if(!root.left&&!root.right) return root.val;
let res1=root.val
if(root.left){
res1+=rob(root.left.left)+rob(root.left.right);
}
if(root.right){
res1+=rob(root.right.left)+rob(root.right.right);
}
let res2=rob(root.left)+rob(root.right);
return Math.max(res1,res2);
};
显然代码超时了,我们计算树的左右孩子和左右子孙的时候出现了重复。那我们考虑下可否用动态规划试试!我们用一个数组(两个单元),下标为0的表示偷该节点的价值,下标1的表示不偷该节点的价值,这样我们就可以解决这题了 三、AC 代码:
初试:
/**
* @param {number[]} nums
* @return {number[]}
*/
var singleNumber = function(nums) {
let xor = 0
for (let i = 0; i < nums.length; i++) {
xor ^= nums[i]
}
let mask = xor & (-xor)//求出最低位的1
let ans = Array(2).fill(0);
for (let i = 0; i < nums.length; i++) {
if ((nums[i] & mask) === 0) {//分组
ans[0] ^= nums[i]
} else {
ans[1] ^= nums[i]
}
}
return ans
}
复试:
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let max=nums[0];
if(nums.length<=3){
return Math.max(...nums);
}
let dp=Array.from(nums);
dp[0]=nums[0];
dp[1]=nums[0];
for(let i=2;i<nums.length-1;i++){
dp[i]=Math.max(nums[i]+dp[i-2],dp[i-1]);
}
max=dp[nums.length-2];
dp[0]=0;
dp[1]=nums[1];
for(let i=2;i<nums.length;i++){
dp[i]=Math.max(nums[i]+dp[i-2],dp[i-1]);
}
return max>dp[nums.length-1]?max:dp[nums.length-1]
};
HR面试
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var rob = function(root) {
let a=robTree(root);
return Math.max(a[0],a[1]);
function robTree(root){
if(!root) return [0,0];
let left = robTree(root.left);
let right =robTree(root.right);
let val1=root.val+left[1]+right[1];//偷了此节点,那么左右都不能偷
let val2=Math.max(left[0],left[1])+Math.max(right[0],right[1]);//这个节点不偷,左右偷还是不偷就取决于,子节点偷还是不偷的价值了。
return [val1,val2];
}
};
四、总结:
当当前状态有依赖于上一个状态时,我们就可以考虑下动态规划。现在你已经是一个合格的小偷了,还写啥程序啊。