news 2026/4/18 7:02:54

从铺地砖到写代码:用骨牌问题带你彻底搞懂动态规划(附Python/Java/C++三种解法)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从铺地砖到写代码:用骨牌问题带你彻底搞懂动态规划(附Python/Java/C++三种解法)

从铺地砖到写代码:用骨牌问题带你彻底搞懂动态规划(附Python/Java/C++三种解法)

周末在家装修,当我盯着那堆1×2的地砖和2×n的走廊发呆时,突然意识到铺砖和动态规划竟然是一回事——每次决定横铺还是竖铺,就像在拆分问题规模。这种生活化思考方式,让我彻底理解了那些曾让我头疼的递推公式。

1. 从厨房地砖到状态转移方程

去年装修厨房时,我面对2×3米的地面,发现用60×120cm的地砖只有三种铺法。这个具体问题后来成了我理解动态规划的钥匙。关键在于最后一步的选择:

  • 竖铺最后一块:前面n-1列的自由度完全保留,方案数等于F(n-1)
  • 横铺最后两块:必须同时覆盖最后两列,方案数等于F(n-2)

这就像玩俄罗斯方块,每个新方块落下时,你只有有限的几种放置选择。用数学表达就是:

F(n) = F(n-1) + F(n-2) (n>2) F(1) = 1, F(2) = 2

这个递推关系与斐波那契数列神似,只是初始条件不同。理解这点,就掌握了动态规划最核心的状态转移思想。

2. 动态规划的三重境界

2.1 暴力递归:最直观的误区

初学者常直接写出这样的递归解法:

def domino_tiling(n): if n == 1: return 1 if n == 2: return 2 return domino_tiling(n-1) + domino_tiling(n-2)

问题:计算F(5)时会重复计算F(3)两次、F(2)三次。时间复杂度呈指数级增长(O(2^n)),n=50时根本算不完。

2.2 记忆化搜索:空间换时间

给递归加个缓存,立刻脱胎换骨:

class Solution { private static long[] memo = new long[51]; public static long dominoTiling(int n) { if (memo[n] != 0) return memo[n]; if (n == 1) return 1; if (n == 2) return 2; memo[n] = dominoTiling(n-1) + dominoTiling(n-2); return memo[n]; } }

优势:时间复杂度降至O(n),但保留了递归的思维模式。适合复杂状态转移的场景。

2.3 递推法:动态规划的终极形态

最经典的DP实现,C++版本展示了极致效率:

#include <iostream> using namespace std; long long dp[51] = {0, 1, 2}; void precompute() { for (int i = 3; i <= 50; ++i) dp[i] = dp[i-1] + dp[i-2]; } int main() { precompute(); int n; while (cin >> n) cout << dp[n] << endl; return 0; }

技巧:预处理打表后,查询只需O(1)时间。注意使用long long防止n=50时溢出(结果达20365011074)。

3. 多语言实现对比

3.1 Python的优雅表达

Python用生成器实现记忆化,展现函数式编程魅力:

from functools import lru_cache @lru_cache(maxsize=None) def domino_tiling(n): return 1 if n == 1 else 2 if n == 2 else \ domino_tiling(n-1) + domino_tiling(n-2)

特点

  • lru_cache装饰器自动处理缓存
  • 代码行数最少,可读性最强
  • 但n>1000时会爆栈(递归深度限制)

3.2 Java的工程化实现

Java版体现了类型安全和面向对象思想:

public class DominoTiler { private static final int MAX_N = 50; private static final long[] dp = new long[MAX_N + 1]; static { dp[1] = 1; dp[2] = 2; for (int i = 3; i <= MAX_N; i++) dp[i] = dp[i-1] + dp[i-2]; } public static long countWays(int n) { if (n < 1 || n > MAX_N) throw new IllegalArgumentException("n must be 1..50"); return dp[n]; } }

亮点

  • 静态初始化块保证线程安全
  • 输入验证体现健壮性
  • 常量定义使参数可配置

3.3 C++的性能优化

C++版本通过模板元编程实现编译期计算:

template<int N> struct Domino { static constexpr long long value = Domino<N-1>::value + Domino<N-2>::value; }; template<> struct Domino<1> { static constexpr long long value = 1; }; template<> struct Domino<2> { static constexpr long long value = 2; }; // 使用示例: cout << Domino<50>::value; // 编译时即计算出结果

适用场景

  • 需要极致性能的嵌入式系统
  • 结果在编译期已知的情况
  • 避免运行时计算开销

4. 动态规划的通用解题框架

通过骨牌问题,我们可以总结出解决DP问题的通用四步法:

  1. 定义状态:明确dp[i]表示什么(这里表示2×i网格的铺法数)
  2. 建立转移方程:分析最后一步的选择(竖铺或横铺)
  3. 确定初始条件:最小子问题的解(dp[1]=1, dp[2]=2)
  4. 计算顺序:从小问题到大问题递推(i从3到n)

将这个框架应用到更复杂的问题,比如用1×1、1×2、1×3的骨牌铺1×n的格子:

问题变体状态转移方程初始条件
标准骨牌问题F(n)=F(n-1)+F(n-2)F(1)=1, F(2)=2
三尺寸骨牌问题F(n)=F(n-1)+F(n-2)+F(n-3)F(1)=1, F(2)=2, F(3)=4
带颜色限制的骨牌F(n)=2F(n-1)+3F(n-2)根据具体限制调整

实际项目中,我曾用这个框架解决过路径规划问题——把机器人移动看作"铺骨牌",每个决策点就是选择不同的"骨牌形状"。

5. 从二维到三维:动态规划的思维跃迁

当问题升级到3×n网格时,状态转移会变得更有趣。此时最后一步可能有:

  • 竖铺1×3的骨牌(上方留空需特殊处理)
  • 横铺两个1×3骨牌叠加
  • L型骨牌的特殊组合

这类问题需要设计更复杂的状态表示,比如引入二进制编码表示每列的填充状态。这提醒我们:动态规划真正的威力在于将看似无限的可能,分解为有限的状态转移。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:01:12

Qwen-Image-Layered效果展示:看AI如何生成可分层编辑的精美图片

Qwen-Image-Layered效果展示&#xff1a;看AI如何生成可分层编辑的精美图片 1. 引言 1.1 技术亮点 Qwen-Image-Layered 代表了图像生成技术的一次重要突破。与传统的单层图像生成不同&#xff0c;它能够将生成的图片自动分解为多个独立的RGBA图层&#xff0c;每个图层都包含…

作者头像 李华
网站建设 2026/4/18 7:01:06

Godot新手必看:图片一缩小就糊?试试在导入设置里勾选这个选项

Godot图像优化指南&#xff1a;彻底解决缩小模糊问题的Mipmaps实战 第一次在Godot里导入精心绘制的像素艺术&#xff0c;满心期待地拖进场景后缩小——结果画面糊成一团&#xff0c;原本清晰的边缘变成了锯齿状的马赛克。这种崩溃感每个Godot开发者都经历过&#xff0c;特别是制…

作者头像 李华
网站建设 2026/4/18 6:59:02

pymongo,一个灵活的 Python 库!

【pymongo&#xff0c;一个灵活的 Python 库&#xff01;】在日常数字化生活中&#xff0c;我们产生的用户信息、聊天记录、文章内容、设备数据、订单日志等信息&#xff0c;大多具有结构不固定、字段灵活、嵌套层级多的特点&#xff0c;传统关系型数据库难以高效存储和查询。而…

作者头像 李华
网站建设 2026/4/18 6:52:32

DAMO-YOLO手机检测部署案例:国产昇腾910B平台适配可行性初探

DAMO-YOLO手机检测部署案例&#xff1a;国产昇腾910B平台适配可行性初探 1. 引言&#xff1a;当手机检测遇上国产算力 想象一下这样一个场景&#xff1a;在工厂的生产线上&#xff0c;摄像头需要实时识别传送带上的每一部手机&#xff0c;检查外观是否有划痕&#xff0c;或者…

作者头像 李华
网站建设 2026/4/18 6:49:27

Matlab多折线图对比分析:从数据到学术图表的一站式实现

1. Matlab多折线图对比分析的核心价值 在科研和学术写作中&#xff0c;数据可视化的重要性怎么强调都不为过。想象一下&#xff0c;你花了几个月时间做实验&#xff0c;收集了大量数据&#xff0c;最后却因为图表表达不清而被审稿人或导师质疑&#xff0c;这该有多郁闷。Matlab…

作者头像 李华