1.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
int rob(vector<int>& nums) {
int n = nums.size();
if (!n) return 0;
int dp[n]; dp[0] = nums[0];
if(n >= 2) dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < n; i++) {
dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);
}
return dp[n - 1];
}
空间优化:
int rob(vector<int>& nums) {
int pre = 0, cur = 0, tmp;
for(int num : nums) {
tmp = cur;
cur = max(pre + num, cur);
pre = tmp;
}
return cur;
}
2.打家劫舍 II
如果房屋都围城一个圈,几第一个和最后一个房屋不能同时打劫,求能偷取的最高金额。
分析:环状排列意味着第一个房子和最后一个房子中只能选择一个偷窃,因此可以把此环状排列房间问题约化为两个 单排排列房间 子问题:
- 在不偷窃第一个房子的情况下(即 nums[1:]),最大金额是 p1
- 在不偷窃最后一个房子的情况下(即 nums[:n-1]),最大金额是 p2
- 综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2) 。
int rob(vector<int>& nums) {
if (nums.empty()) return 0;
if(nums.size() == 1) return nums[0];
vector<int> dp1(nums.begin(), nums.end() - 1);
vector<int> dp2(nums.begin() + 1, nums.end());
return max(myRob(dp1), myRob(dp2));
}
int myRob(vector<int> nums) {
int pre = 0, cur = 0, tmp;
for(int num : nums) {
tmp = cur;
cur = max(pre + num, cur);
pre = tmp;
}
return cur;
}
3.打家劫舍III
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
分析:
res[0]表示不偷盗跟节点,res[0]表示偷盗跟节点,则
res[0] = max(left[0], left[1]) + max(right[0], right[1])
res[1] = root->val + left[0] + right[0]
//res[0]:norob; res[1]:rob
vector<int> dfs(TreeNode* root) {
if (!root) return {0, 0};
auto left = dfs(root->left);
auto right = dfs(root->right);
return {max(left[0], left[1]) + max(right[0], right[1]), root->val + left[0] + right[0]};
}
int rob(TreeNode* root) {
vector<int> res = dfs(root);
return max(res[0], res[1]);
}