动态规划:求解巧克力棒分割的最少切割次数问题
最少切割次数分割巧克力棒(动态规划解法)
嘿,这个问题用动态规划来解决完全没问题,我给你一步步拆解清楚:
问题再梳理下
先把需求明确下,避免理解偏差:
现有一块$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
相关产品推荐
相关产品推荐

