You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态规划:求解巧克力棒分割的最少切割次数问题

最少切割次数分割巧克力棒(动态规划解法)

嘿,这个问题用动态规划来解决完全没问题,我给你一步步拆解清楚:

问题再梳理下

先把需求明确下,避免理解偏差:

现有一块$F\times C$的巧克力棒,部分方格带杏仁。只能沿水平或垂直方向切割:水平切会把巧克力分成$k\times C$和$(F-k)\times C$两块;垂直切分成$F\times k$和$F\times (C-k)$两块。我们要找到把原巧克力拆成若干纯块(要么全带杏仁,要么全不带)所需的最少切割次数。

动态规划核心思路

1. 先做预处理:快速判断子矩形是否为纯块

首先我们需要一个能快速判断任意子矩形是不是纯块的方法,这里用前缀和数组来实现最方便:

  • 先创建一个二维前缀和数组prefix,其中prefix[i][j]代表从巧克力左上角(0,0)到(i-1,j-1)这个区域内的杏仁总数(假设坐标从0开始计数)。
  • 对于任意子矩形(左上角$(x1,y1)$,右下角$(x2,y2)$),它的杏仁总数可以通过前缀和公式计算:
    total = prefix[x2+1][y2+1] - prefix[x1][y2+1] - prefix[x2+1][y1] + prefix[x1][y1]
    
  • 如果total == 0(全不带杏仁)或者total == (x2-x1+1)*(y2-y1+1)(全带杏仁),那这个子矩形就是纯块,不需要切割。

2. 状态定义

我们定义dp[x1][y1][x2][y2]为:把左上角$(x1,y1)$到右下角$(x2,y2)$的子巧克力块,拆成纯块所需的最少切割次数。

3. 状态转移方程

如果当前子矩形已经是纯块,那dp[x1][y1][x2][y2] = 0,这是边界条件。

如果不是纯块,我们有两种切割选择,取两种选择里的最小值:

  • 水平切割:遍历所有可能的切割位置$k$(在第$k$行下方切,把矩形分成上下两部分),总切割次数为「上半部分的最少切割次数 + 下半部分的最少切割次数 + 1」(+1是因为这次切割本身算一次操作)。我们要遍历所有合法的$k$,取最小的那个值。
  • 垂直切割:同理,遍历所有可能的切割位置$k$(在第$k$列右侧切,分成左右两部分),总切割次数为「左半部分的最少切割次数 + 右半部分的最少切割次数 + 1」,同样取所有$k$中的最小值。

用公式写出来就是:

dp[x1][y1][x2][y2] = min(
    # 所有水平切割的最小次数
    min( dp[x1][y1][k][y2] + dp[k+1][y1][x2][y2] + 1 )  for k in x1 <= k < x2,
    # 所有垂直切割的最小次数
    min( dp[x1][y1][x2][k] + dp[x1][k+1][x2][y2] + 1 )  for k in y1 <= k < y2
)

4. 计算顺序

因为大的矩形依赖于更小的子矩形,所以我们需要按照矩形面积从小到大来计算:先算所有1x1的小方块(都是纯块,dp值为0),然后算面积为2的矩形,直到计算到整个$F\times C$的原巧克力。

当然,你也可以用记忆化搜索的方式来实现(递归+缓存),这样不用手动排序,逻辑更直观:写一个递归函数,输入矩形的四个坐标,先判断是不是纯块,是就返回0;否则检查缓存里有没有已经算好的值,有就直接返回;没有就遍历所有切割方式计算最小值,存入缓存后返回。

实现注意点

  • 前缀和数组的坐标要搞清楚,避免越界错误。
  • 时间复杂度方面:对于$F\times C$的巧克力,子矩形的数量是$O(F2C2)$,每个子矩形需要遍历$O(F+C)$次切割位置,总时间复杂度是$O(F3C2 + F2C3)$,对于$F,C\leq50$的情况完全没问题,更大的规模可能需要优化,但一般题目不会卡这么死。
  • 可以用四维数组来存储dp值,或者用字典来做缓存(记忆化搜索时)。

内容的提问来源于stack exchange,提问作者Álvaro G. Tenorio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:26:08