程序员的算法趣题Q59: 合并单元格的方式

本文介绍了如何解决一个关于单元合并的算法问题,该问题是从一个4x4的网格开始,考虑不同尺寸的长方形或正方形进行铺设。作者提到这个问题与之前的Q32问题相似,可以通过修改之前的解决方案来解答。通过行扫描和递归方法,找到了所有可能的合并方案,得出共有70878种。文章还提到了一个问题的扩展,即不允许存在1x1的未合并单元,这种情况下的解决方案数量为1208种。
摘要由CSDN通过智能技术生成

目录

1. 问题描述

2. 解题分析

3. 代码及测试

4. 后记


1. 问题描述

2. 解题分析

        似曾相似燕归来。。。

        第一感就认出了这道题目跟之前“Q32:榻榻米的铺法”的相似之处,本题完全可以看作是Q32的增强版。在Q32中,榻榻米的尺寸是固定的1x2的大小。而本题可以看作是用任意尺寸长方形或正方形对房间铺设的问题!

        基于这一认识,可以直接基于Q32的题解进行一些适当修改就可以的得到本题的题解。喜欢挑战的小伙伴如果没有做过前面的Q32(或者做过了但是记忆模糊了)的话,可以先独立做做这道题目,如果卡住了再回头去看一下Q32的题解看看能不能得到启发。最后还是卡住了的话,再看以下题解。

        基于Q32的题解方案,有以下要点:

  1. 外加围栏,以方便判断
  2. 行扫描(或者列扫描也可以,道理相同),以很小的冗余扫描代价来换取巨大的判断简化的利益
  3. 从某一个点出发的可能的合并(对应于榻榻米铺法)方法的遍历。

        第3点这是本题与Q32的本质的差异点。这里就不解释了,直接看代码很简单明了。单就代码而言甚至比Q32的还要简单。

        其中利用到了numpy.any()函数用于判断某矩形区域内是否全0。因为本题中并没有要求给出合并方案的图示化(事实上也不可能,因为种类数之多远远超出了直感),所以在递归调用时没有给合并块(“榻榻米”)编号并标记到格点内,而只是简单地用“0”表示空格,“1”表示已经被合并的格子。

3. 代码及测试

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 16 08:23:03 2021

@author: chenxy
"""

import sys
import time
import datetime
import math
# import random
from   typing import List
# from   queue import Queue
# from   collections import deque
import itertools as it
import numpy as np

H = 4 # Height, vertical
W = 4 # Width,  horizontal

# cells initialization, with a guard band surrounding the original cells
# The guard band is initialized to '-1' to simplify the judgement processing.
cells = np.zeros((H+2, W+2))
cells[0,:] = -1
cells[H+1,:] = -1
cells[:,0] = -1
cells[:,W+1] = -1

count = 0

def combine_cells(h,w)->int:
    '''
    Parameters
    ----------
    (h,w) : The current exploration point. 
            h represents row index, w represents col index.
    Returns: int
        The number of total arrangement starting from the point (h,w), together 
        with the current cells status, which is a global variable

    '''        
    global count
    
    # print(h,w,idx)
    if   h == H + 1:
        count = count + 1
        # print(cells)    
    elif w == W + 1: 
        # Reach the right boundary, go to explore the next row from the left 
        combine_cells(h+1, 1)
    elif cells[h,w] > 0: 
        # This grid has been occupied, move to the right one
        combine_cells(h, w+1)
    else:
        for i in range(h,H+1):
            for j in range(w,W+1):
                # Judge whether (h,w)--(i,j) ractangulat can be combined.
                # Here, (h,w) represents top-left corner, (i,j) represent right-down corner
                if ~np.any(cells[h:i+1,w:j+1]):
                    cells[h:i+1,w:j+1] = 1
                    combine_cells(h, j+1)
                    cells[h:i+1,w:j+1] = 0
                            
tStart = time.perf_counter()
combine_cells(1, 1)
tCost  = time.perf_counter() - tStart
print('count = {0}, tCost = {1:6.3f}(sec)'.format(count,tCost))  

        运行结果:count = 70878, tCost =  1.471(sec) 

        

4. 后记

        原书中给出了好几页的分析和题解。没有耐心看(反正已经有了个解垫底^-^),而且可能也很难看懂。这种问题思路上没有对上路的话,尽量别人blabla说一大堆估计也很难跟得上。相信很多小伙伴们都会有同感。

        尽管这道题目在本书中是属于高级篇,但是基于Q32我几乎瞬间就得出的解决方案。由此也说明循序渐进的积累的重要性。

        这个结果非常反直觉,小小的4*4的网格区间的单元合并居然能够整出这么多种的可能性,真的是很神奇。

        另外,本题还有个进一步的要求是,求最后不包含1x1尺寸格子(也就是不允许有未合并的单元格)的合并方案数。这个只需要在以上题解中稍作条件修改就可以得到。留给有兴趣的小伙伴们自己尝试,答案是1208种。

        上一篇:Q58: 丢手绢游戏中的总移动距离

        下一篇:Q60: 分割为同样大小

        本系列总目录参见:程序员的算法趣题:详细分析和Python全解

        

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值