1. 题⽬链接:213.打家劫舍II
2. 题⽬描述:
3. 解法(动态规划)
算法思路:
这⼀个问题是「打家劫舍I」问题的变形。
上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但 是我们可以将「环形」问题转化为「两个单排」问题:
a. 偷第⼀个房屋时的最⼤⾦额x ,此时不能偷最后⼀个房⼦,因此就是偷[0, n - 2] 区间 的房⼦;
b. 不偷第⼀个房屋时的最⼤⾦额y ,此时可以偷最后⼀个房⼦,因此就是偷[1, n - 1] 区 间的房⼦;
两种情况下的「最⼤值」,就是最终的结果。 因此,问题就转化成求「两次单排结果的最⼤值」。
C++算法代码:
class Solution
{
public:
int n;
//返回区间内能偷到的最大值
int Max_num(vector<int>& nums,int i,int j)
{
//边界情况
if(i>j)
{
return 0;
}
//建表
vector<int>f(n); //该点偷之后的最大金额
vector<int>g(n); //该点不偷之后的最大金额
//初始化
f[i]=nums[i],g[i]=0;
//填表
for(int k=i+1;k<=j;k++)
{
f[k]=g[k-1]+nums[k];
g[k]=max(g[k-1],f[k-1]);
}
//返回值
return max(f[j],g[j]);
}
int rob(vector<int>& nums)
{
n=nums.size();
return max(Max_num(nums,2,n-2)+nums[0],Max_num(nums,1,n-1));
}
};
Java算法代码:
class Solution
{
public int rob(int[] nums)
{
int n = nums.length;
return Math.max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));
}
public int rob1(int[] nums, int left, int right)
{
if (left > right) return 0;
// 1. 创建 dp 表
// 2. 初始化
// 3. 填表
// 4. 返回
int n = nums.length;
int[] f = new int[n];
int[] g = new int[n];
f[left] = nums[left];
for (int i = left + 1; i <= right; i++)
{
f[i] = g[i - 1] + nums[i];
g[i] = Math.max(g[i - 1], f[i - 1]);
}
return Math.max(f[right], g[right]);
}
}