剑指offer面试题牛客_递归和循环_矩形覆盖(java版)

本文介绍了一道剑指offer的面试题,探讨如何使用2*1的小矩形无重叠地覆盖一个2*n的大矩形。通过动态规划的方法,作者解析了递归式,并指出这个问题可以理解为斐波那契数列,空间复杂度达到O(1)。文中强调了在解决此类问题时,应根据放置方式而非小矩形数量进行分类。

welcome to my blog

剑指offer面试题牛客_递归和循环_矩形覆盖(java版):

题目描述

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

思路 (2020.2.11, 再看看这个思路: DP中的选或者不选其实是选方法1还是选方法2)

  • 动态规划中,推到递归式的时候,有个方法时:
    • 选或者不选, 其实更准确地说,
    • 从两个可能中选择一个, 选方法1还是选方法2
    • 具体到本题就是: 选竖着的小矩形, 还是选横着的小矩形
  • 为了不产生混乱,应该这样理解递推式,从左向右看大矩形(规定个方向):
    • 选择竖着放小矩形,那么只能在f(3)右侧放一块
    • 选择横着放小矩形,那么只能在f(2)右侧放两块
    • 其实就是个斐波那契数列f(n) = f(n-1) + f(n-2)
第二次做 空间复杂度O(1) 最大的收获
  • 最大的收获是知道该如何分类了: 在只使用一种方式的情况下去探索递推公式
  • 跳台阶,两种方式:一次跳一阶; 一次跳两阶
  • 矩形覆盖, 两种方式: 横着覆盖; 竖着覆盖。 而不是按照使用几块小矩形去分类
  • 有几种方式? 题目会清晰地给出
  • 初始化→(执行→更新)^n
/*
在只用一种方式(横着或者竖着)去覆盖的情况下,探索递推公式
n=1,f(1)=1
n=2,f(2)=2
n>2,f(n)= f(n-1) + f(n-2)
*/
public class Solution {
    public int RectCover(int target) {
        if(target==1)
           return 1;
        if(target==2)
           return 2;
        int a=1, b=2, res=0;
        for(int i=3; i<=target; i++){
            res = a + b;
            a = b;
            b = res;
        }
        return res;
    }
}
斐波那契序列, 循环版
public class Solution {
    public int RectCover(int target) {
        /*
        思路:本质上 n 覆盖方法种类都是对 n - 1 时的扩展。这句话说得不准确(因为可能也是对n-2的扩展), 但要体会这句话的思想
            动态规划中,推到递归式的时候,有个方法时:选或者不选, 其实更准确地说, 从两个可能中选择一个, 选方法1还是选方法2
            先找规律, 设f(n)表示2*n的矩形的覆盖方法
            f(1)=1
            f(2)=2
            怎么得到f(n)? 如果选方法1:竖着放1块小矩形,那么就是f(n-1)种方式; 如果选方法2:横着放2块小矩形,那么就是f(n-2)种方式
            所以f(n) = f(n-1) + f(n-2)
            其实还有一个疑问: 比如f(4) = f(3) + f(2), 其中,f(3)再竖着放一个小矩形就能得到f(4),
            会不会有排列组合的问题? 比如竖着的小矩形放在f(3)的左边和右边, 实际上是没有问题的, 
            f(3)右边放竖着的小矩形得到f(4), 如果问f(3)左边放竖着的小矩形怎么办? 答案在f(2)中, 
            其实f(3)+f(2)和涵盖了所有小矩形排列组合的情况, 为了不产生混乱,应该这样理解递推式,**从左向右看大矩形(规定个方向)**:
            选择竖着放小矩形,那么只能在f(3)右侧放一块
            选择横着放小矩形,那么只能在f(2)右侧放两块
            其实就是个斐波那契数列f(n) = f(n-1) + f(n-2)
        */
        //input check
        if(target < 1) return 0;
        //execute
        if(target == 1) return 1;
        if(target == 2) return 2; // 不要算错
        int[] arr = new int[target+1];
        arr[1] = 1;
        arr[2] = 2;
        for(int i=3; i<=target; i++)
            arr[i] = arr[i-1] + arr[i-2];
        return arr[target];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值