剑指offer-10、矩阵覆盖

题目描述

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

比如n=3时,2 * 3 的矩形块有3种覆盖方法:

思路及解答

我们需要用若干个2×1的小矩形(可以横放或竖放)无重叠地覆盖一个2×n的大矩形,求总共有多少种不同的覆盖方法。例如当n=3时,共有3种覆盖方法。

通过观察小规模案例,我们可以发现:

  • n=1时,只有1种方法(竖放)
  • n=2时,有2种方法(两个竖放或两个横放)
  • n=3时,有3种方法
  • n=4时,有5种方法

显然,这形成了一个类似斐波那契数列的规律:f(n) = f(n-1) + f(n-2)。这一题其实和上面青蛙跳台阶和斐波那契数列是一样的,变的只是场景。

递归

递归是解决这类问题最直观的方法。对于2×n的矩形,我们考虑第一块小矩形的放置方式:

  1. 如果竖着放,则剩下的部分是2×(n-1)的矩形,有f(n-1)种方法
  2. 如果横着放,则下方也必须横放一块,剩下的部分是2×(n-2)的矩形,有f(n-2)种方法

因此,总方法数为这两种情况之和:f(n) = f(n-1) + f(n-2),这正是斐波那契数列的递推关系

java 复制代码
public class Solution {
    public int rectCover(int n) {
        if (n <= 0) return 0;
        if (n == 1) return 1;
        if (n == 2) return 2;
        return rectCover(n - 1) + rectCover(n - 2);
    }
}
  • 时间复杂度:O(2^n),存在大量重复计算
  • 空间复杂度:O(n),递归调用栈深度

动态规划

在递归解法基础上,我们可以加入记忆化存储,避免重复计算。使用一个数组存储已经计算过的结果,每次计算前先检查是否已存储

java 复制代码
public class Solution {
    
    public int rectCover(int n) {
        if (n <= 0) return 0;
        if (n == 1) return 1;
        if (n == 2) return 2;
        
        int[] dp = new int[n + 1];

		for (int i = 2; i <= n; i++) {
			dp[i] = dp[i - 1] + dp[i - 2];
		}
		
		return dp[n];
    }
}
  • 时间复杂度:O(n),每个子问题只计算一次
  • 空间复杂度:O(n),需要存储中间结果

优化的动态规划(空间优化)

观察发现,计算f(n)只需要前两个状态f(n-1)和f(n-2),因此可以用两个变量代替整个数组,将空间复杂度优化到O(1)。

java 复制代码
public class Solution {
    public int rectCover(int n) {
        if (n <= 0) return 0;
        if (n == 1) return 1;
        if (n == 2) return 2;
        
        int prev1 = 1, prev2 = 2;
        int result = 0;
        for (int i = 3; i <= n; i++) {
            result = prev2 + prev1;
            prev1 = prev2;
            prev2 = result;
        }
        return result;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
相关推荐
会编程的林俊杰12 小时前
SpringBoot项目启动时的依赖处理
java·spring boot·后端
一叶飘零_sweeeet12 小时前
深度拆解汽车制造系统设计:用 Java + 设计模式打造高扩展性品牌 - 车型动态生成架构
java·设计模式·工厂设计模式
王家羽翼-王羽12 小时前
nacos 3.1.0 运行主类报错 com.alibaba.cloud.nacos.logging.NacosLoggingAppRunListener
java
影子240113 小时前
oralce创建种子表,使用存储过程生成最大值sql,考虑并发,不考虑并发的脚本,plsql调试存储过程,java调用存储过程示例代码
java·数据库·sql
武子康13 小时前
Java-172 Neo4j 访问方式实战:嵌入式 vs 服务器(含 Java 示例与踩坑)
java·服务器·数据库·sql·spring·nosql·neo4j
程序猿DD13 小时前
深入探索剖析 JVM 的启动过程
java
Arva .14 小时前
ConcurrentHashMap 的线程安全实现
java·开发语言
听风吟丶14 小时前
Java 9+ 模块化系统(Jigsaw)实战:从 Jar 地狱到模块解耦的架构升级
java·架构·jar
昂子的博客14 小时前
Redis缓存 更新策略 双写一致 缓存穿透 击穿 雪崩 解决方案... 一篇文章带你学透
java·数据库·redis·后端·spring·缓存