线性规划--状态转移(打家劫舍)

打家劫舍I

1.题目

2.思路

要解决这个问题,我们可以使用动态规划的方法。我们将问题分为两个子问题:偷窃前n-1家或者偷窃前n-2家。如果我们选择偷窃第n家,那么我们就不能偷窃第n-1家,因为它们是相邻的。所以,我们可以得到如下的递推关系:

令dp[i]表示偷窃前i家能够得到的最高金额,则

  • 如果我们不偷窃第i家,那么dp[i] = dp[i-1];
  • 如果我们偷窃第i家,那么dp[i] = dp[i-2] + nums[i],其中nums[i]表示第i家房屋的金额。

最终的答案就是dp[n-1]和dp[n-2]中的较大值,因为我们可以选择偷窃最后一家或者不偷窃最后一家。

3.代码



public class 打家劫舍 {
	
	//令dp[i]表示偷窃前i家能够得到的最高金额,则
	//如果我们不偷窃第i家,那么dp[i]=dp[i-1]
	//如果我们偷窃第i家,那么dp[i]=dp[i-2]+nums[i],nums[i]表示第i家房屋的金额
	//最终答案就是dp[n-1],dp[n-2]中的较大值
	public int rob(int[] nums) {
		if(nums==null||nums.length==0) {
			return 0;
		}
		if(nums.length==1) {
			return nums[0];
		}
		if(nums.length==2) {
			return Math.max(nums[0], nums[1]);
		}
		
		int prev2=nums[0];
		int prev1=Math.max(nums[0], nums[1]);
		
		for(int i=2;i<nums.length;i++) {
			int curr = Math.max(prev1, prev2+nums[i]);
			prev2=prev1;
			prev1=curr;
		}
		return prev1;
		
	}
	public static void main(String[] args) {
		打家劫舍 robber = new 打家劫舍();
		int [] nums= {1,2,3,1};
		System.out.println(robber.rob(nums));
	}
}



4.知识

    1)令dp[i]表示偷窃前i家能够得到的最高金额,则
    如果我们不偷窃第i家,那么dp[i]=dp[i-1]
    如果我们偷窃第i家,那么dp[i]=dp[i-2]+nums[i],nums[i]表示第i家房屋的金额
    最终答案就是dp[n-1],dp[n-2]中的较大值

2)代码重点:

        int prev2=nums[0];
        int prev1=Math.max(nums[0], nums[1]);

        for(int i=2;i<nums.length;i++) {
            int curr = Math.max(prev1, prev2+nums[i]);
            prev2=prev1;
            prev1=curr;
        }
        return prev1;

我们使用了两个变量prev2prev1来存储前两个房屋的最大偷窃金额,然后遍历数组中的每个房屋,更新这两个变量的值。最后返回prev1,它代表偷窃前n-1家房屋的最大金额。

3)   if(nums==null||nums.length==0) {
            return 0;
        }

先判断数组的是否为空或者长度为零
        if(nums.length==1) {
            return nums[0];
        }

若数组长度为1,直接返回第一个元素的大小
        if(nums.length==2) {
            return Math.max(nums[0], nums[1]);
        }

若数组长度为2,比较两个元素,返回较大的那个元素的值

打家劫舍II

1.题目

2.思路

与上面的“打家劫舍I”唯一的不同就是:房屋围成一圈。

为了解决这个问题,我们可以将问题分为两个子问题来解决:

  1. 偷窃从第一个房屋到倒数第二个房屋的最大金额(即排除最后一个房屋)。
  2. 偷窃从第二个房屋到最后一个房屋的最大金额(即排除第一个房屋)。

然后,取这两个子问题的解中的较大值作为最终的结果。

3.代码

package 动态规划;  
  
public class 打家劫舍II {  
    public int rob(int[] nums) {  
        if (nums == null || nums.length == 0) return 0;  
        if (nums.length == 1) return nums[0];  
        if (nums.length == 2) return Math.max(nums[0], nums[1]);  
  
        int n = nums.length;  
        // dp[i][0] 表示偷窃前 i 个房屋(不包括第 i 个房屋)时的最大金额  
        // dp[i][1] 表示偷窃前 i 个房屋(包括第 i 个房屋)时的最大金额  
        int[][] dp = new int[n][2];  
        dp[0][0] = 0;  
        dp[0][1] = nums[0];  
        dp[1][0] = nums[0];  
        dp[1][1] = Math.max(nums[0], nums[1]);  
  
        for (int i = 2; i < n - 1; i++) {  
            // 不偷窃第 i 个房屋时,最大金额是前 i-1 个房屋的最大金额  
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);  
            // 偷窃第 i 个房屋时,最大金额是前 i-2 个房屋的最大金额加上第 i 个房屋的金额  
            dp[i][1] = dp[i - 1][0] + nums[i];  
        }  
  
        // 对于最后一个房屋,我们不能偷窃它,因为房屋是排列成一个圈的  
        // 所以我们返回偷窃前 n-1 个房屋的最大金额  
        return Math.max(dp[n - 2][0], dp[n - 2][1]);  
    }  
  
    public static void main(String[] args) {  
        打家劫舍II solution = new 打家劫舍II();  
        int[] nums = {2, 3, 2};  
        System.out.println( solution.rob(nums));  
    }  
}

4.知识

     1)   int n = nums.length;  
        // dp[i][0] 表示偷窃前 i 个房屋(不包括第 i 个房屋)时的最大金额  
        // dp[i][1] 表示偷窃前 i 个房屋(包括第 i 个房屋)时的最大金额  

        int[][] dp = new int[n][2];  
        dp[0][0] = 0;  
        dp[0][1] = nums[0];  
        dp[1][0] = nums[0];  
        dp[1][1] = Math.max(nums[0], nums[1]);  

既然该题用二维数组的方式解,就不能只写 dp[0][0]和dp[0][1],会出错

2)       for (int i = 2; i < n - 1; i++) {  
            // 不偷窃第 i 个房屋时,最大金额是前 i-1 个房屋的最大金额  
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);  
            // 偷窃第 i 个房屋时,最大金额是前 i-2 个房屋的最大金额加上第 i 个房屋的金额  
            dp[i][1] = dp[i - 1][0] + nums[i];  
        }  

  • 33
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值