题目描述:
leetcode198
你是一个专业的强盗,计划去抢一条街上的房子。每个房子里都有一定量的钱,唯一限制你抢掉所有房子里的钱的是:相邻的房子的安保系统是连接在一起的,如果相邻的两个房子在同一晚上都被盗的话就会自动报警。给出一系列非负的整数代表每个房子内的金钱数量,计算你今晚不触发报警条件的情况下可以抢到的最多的钱。
分析:
共有n家店抢,不能连续抢两家求如何抢最多。相当于给你一个数组int[] nums 可以任意取几个值求最大和 ,但是数组不能取相邻的。
设计暴力算法其他的先不管先把题做出来
/**
*
* @param nums 数组用于存储每家店里有的钱数
* @return 抢到的最大值
*/
public int rob(int[] nums)
{
return solve(nums.length-1,nums);
}
/**
* 思路:
* 抢到第index家店时总金额有两种可能
* 1.这家店抢
* 则抢劫的总金额为 这家店有的钱nums[index]加上抢到第index-2家时总金额 solve(index-2,nums)
* 2.这家店不抢
* 因为不能相邻则小偷可以抢到index-1家店 则这时总金额solve(index-1,nums)
* 因为要求总金额最大值
* 则比较两种可能返回较大的那个
*
* @param index 数组索引相当于店的门牌号
* @param nums 数组用于存储每家店里有的钱数
* @return 抢到index家店时的最大金额
*/
private int solve(int index, int[] nums) {
if(index<0)//边界条件
{
return 0;
}
//抢到第index家店时最大值有两种可能选择较大的返回
return Math.max(nums[index]+solve(index-2,nums),
solve(index-1,nums));
}
理论上是可以得出结果但是实际上因为每家店都有抢不抢两种可能时间复杂度为2^n过大通不过
n->(n-2,n-3,n-4) 状态n由前n-2个状态决定 前n-2个状态每个都要算一遍
n-1->(n-3,n-4,n-5) 状态n-1由前n-3个状态决定 前n-3个状态每个都要算一遍
这其中有大量重复状态的冗余计算使得时间复杂度升高为了解决这个问题可以用一个数组存储已经计算过的状态改进算法。思路与上面的一样
public static int[] res;//存储抢到第index个店时的最大值
public int rob2(int[] nums)
{
//初始化备忘录
res=new int[nums.length];
for(int re:res)
{
re=-1;
}
return res[nums.length-1];
}
private int solve2(int index, int[] nums)
{
if(index<0)
{
return 0;
}
if(res[index]>=0)//判断抢到第index店的最大值有没有算过算过就直接取值没有就往下算
{
return res[index];
}
//将抢到第index店的最大值存储起来防止重复计算
res[index]= Math.max(nums[index]+solve(index-2,nums),
solve(index-1,nums));
return res[index];
}
有了备忘录记录状态共有n个状态每个算一遍时间复杂度为n此时就能通过了
使用递推的方式来做此题
由已知的简单的情况开始推
1 如果有0家店
return 0
2 如果有1家店
return nums[0]
3 如果有2家店
return Math.max(nums[0],nums[1])
4 如果有3家店
则有两种可能
1这家店抢 总金额为 nums[2]+nums[0]
2这家店不抢 总金额为 nums[1]
比较两种可能可以知道这家店抢还是不抢
由此我们可以总结出规律第index家店抢或不抢由抢到第index-2家时的最大值res[index-2](这里我们用数组res来记录抢到第index家时的最大值)加上第index家有的钱nums[index] 即 总金额 =nums[index]+res[index-2]
与不抢第index家 总金额=res[index-1]
相比哪个大 则res[index]=总金额较大的那个
相同的规律退出index+1家的情况直到抢到最后一家
/**
*
* 递推
*
*
*/
public int rob3(int[] nums)
{
if(nums.length==0)
{
return 0;
}
if(nums.length==1)
{
return nums[0];
}
//初始化备忘录
res=new int[nums.length];
res[0]=nums[0];
res[1]=Math.max(nums[0], nums[1]);
for(int i=2;i<nums.length;i++)
{
//抢到第i家时的最大值为两种情况的最大值
//这家抢则最大值等于第i家抢的值加上抢到第i-2家时的最大值
res[i]= Math.max(nums[i]+res[i-2],
//这家不抢则最大值和抢到第i-1家时的最大值相同
res[i-1]);
}
return res[nums.length-1];
}
此方法时间复杂度为n但是与递归相比递推要多考虑许多边界条件相对麻烦了一些。
参考资料【https://www.bilibili.com/video/av19345520/】