延展问题=基本问题+特殊情况

来源:互联网 发布:opencv图像处理算法 编辑:程序博客网 时间:2024/06/14 09:20

  对于一些在原问题基础上增加了一些条件,进而延展一个新问题的题目,往往可以视为基本问题加上特殊情况的处理。
  先回顾一下之前有一篇博文提到的抢劫问题,是对一条线上的房子进行抢劫。
  
   LeetCode 198题

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.  

  以O(n)时间遍历数组,每一步可以获得的信息:
  第一,上一步执行抢劫,所获得的总金钱;
  第二,上一步不执行抢劫,所获得的总金钱;
  第三,这一步执行抢劫,所获得的总金钱;
  第四,这一步不执行抢劫,所获得的总金钱;
  因此,第二项加上当前抢劫金额的和即为第三项,将第一、第二进行比较,较大者即为第四项的答案。遍历结束,比较第三和第四项,即为所要的答案。

Java代码如下:
public int rob(int[] num) {    int[][] dp = new int[num.length + 1][2];    for (int i = 1; i <= num.length; i++) {        dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);        dp[i][1] = num[i - 1] + dp[i - 1][0];    }    return Math.max(dp[num.length][0], dp[num.length][1]);}

  再让我们看一下这个问题的延展问题。LeetCode 213题。
  
   LeetCode 213题

Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. 

  这个延展问题是在基本问题的基础上,把房子的排列从一条线变成了一个圆。这是两道题在题设上唯一不同的地方。基本问题我们已经解决了,那么延展问题该如何切入呢?
  延展问题 = 基本问题 + 特殊情况。
  特殊情况是什么,就是这个圈。上面的公式变一变,也就是如下:
  延展问题 - 特殊情况 = 基本问题
  把延展问题中的圈变成线,是不是就可以按照基本问题处理了呢?
  当然,在拆圈成线的过程中还是需要一些思考的。这个圈上每一个节点在位置上都是等价的,所以在哪里拆并不重要。拆的话,就是要把某一对邻居节点变成非邻居状态。假设我们拆 i 和 i+1 节点。在圈状态,由于抢劫 i 就不能抢劫 i+1,相当于只能在 i 转一圈到 i+2 的这 n-1 个节点中实施抢劫计划。选择抢劫 i ,那么拆成线之后,就可以将 i+2 作为初始节点, i 作为最终节点,利用基本问题的方案执行即可。同理,选择抢劫 i+1 ,那么拆成线之后,就可以将 i+1 作为初始节点, i-1 作为最终节点,利用基本问题的方案执行即可。
  为了方便,我们选择最后一个节点和初始节点之间作为拆线的位置,执行两次拆线。

Java代码如下:
public int rob(int[] nums) {    if (nums.length == 1) return nums[0];    return Math.max(rob(nums, 0, nums.length - 2), rob(nums, 1, nums.length - 1));//两次拆线}//基本问题private int rob(int[] num, int lo, int hi) {    int include = 0, exclude = 0;    for (int j = lo; j <= hi; j++) {        int i = include, e = exclude;        include = e + num[j];        exclude = Math.max(e, i);    }    return Math.max(include, exclude);}
原创粉丝点击