ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
Python自动化办公应用大全 Excel 2021函数公式学习大典 Kutools for Office 套件发布 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: 赵桢0611

[分享] 伤脑筋十二块的拼成长方形

[复制链接]

TA的精华主题

TA的得分主题

发表于 2025-8-12 15:47 | 显示全部楼层
micch 发表于 2025-8-10 22:17
有做过2048的话,两个任意图形的拼接可以用2048的逻辑去处理,但是12个图形的拼接,就需要算法了,很复杂

如果按楼下的1/0逻辑来数字化图块(有为1,无为0)的话,每格的和为0表示空,和为2表示已重叠而跳出循环。最终目的视为把1填满5*12或6*10的格子。不知道12 block puzzle之类的解题逻辑是什么?这对VBA来说是不是太难太慢了。

TA的精华主题

TA的得分主题

发表于 2025-8-12 21:59 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
henterwu 发表于 2025-8-12 15:47
如果按楼下的1/0逻辑来数字化图块(有为1,无为0)的话,每格的和为0表示空,和为2表示已重叠而跳出循环 ...

算法和语言无关,vba也许不如其他语言,但是如果算法有了,那就vba能实现。

关键不是如何判断是否组成长方形,关键是两个形状拼接组合有好几种模式(每个形状有多重形态),再增加一个形状,那么拼接方式是几何式增长,增加到12个有太多的拼接方案,如果用暴力组合的方式不现实。

程序运算慢不慢,不分语言,vba实质上也不会比C就慢

TA的精华主题

TA的得分主题

发表于 2025-8-12 22:13 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 yynrzwh 于 2025-8-12 22:15 编辑

仅算6*10,并取1个路径
例如:[1,1][2,1][5,1][8,2][7,1][6,4][10,4][11,2][3,3][9,4][4,3][12,4]
[1,1]中,前面的1表示第一种图形,后面的1表示第一种旋转方式。
十二块凑长方形.7z (25.21 KB, 下载次数: 9)
image.png
  1. Dim n&, ar, spath$, rol&
  2. Sub test2()
  3. n = 12
  4. ReDim ar(1 To n, 1 To 4)
  5. getblock ar
  6. ReDim res(1 To 6, 1 To 10) As Integer '6*10
  7. ReDim u(1 To n) As Boolean
  8. spath = "": rol = 0
  9. dfs res, u, "", False
  10. MsgBox spath
  11. End Sub
  12. Private Sub dfs(res, u, ss, flag)
  13. pos = getpos(res)
  14. x = pos(0): y = pos(1)
  15. If x = 0 Then
  16. '    rol = rol + 1
  17. '    Cells(rol, "w") = ss
  18.     spath = ss
  19.     flag = True
  20.     Exit Sub
  21. End If
  22. For i = 1 To n
  23.     If Not u(i) Then
  24.     For j = 1 To 4
  25.         b = ar(i, j)
  26.         If IsArray(b) Then
  27.             tem = res
  28.             If istrue(x, y, b, tem) Then
  29.                 u(i) = True
  30.                 dfs tem, u, ss & "[" & i & "," & j & "]", flag
  31.                 If flag Then Exit Sub
  32.                 u(i) = False
  33.             End If
  34.         End If
  35.     Next
  36.     End If
  37. Next
  38. End Sub
  39. Private Function istrue(x, y, b, tem) As Boolean
  40. Dim r&, c&, k&
  41. For i = 1 To UBound(b, 2)
  42.     If b(1, i) = 1 Then Exit For
  43.     k = k + 1
  44. Next
  45. si = x
  46. sj = y - k
  47. m1 = x + UBound(b) - 1
  48. m2 = y + UBound(b, 2) - 1 - k
  49. If m1 > UBound(tem) Or m2 > UBound(tem, 2) Or sj < 1 Then Exit Function
  50. For i = si To m1
  51.     r = r + 1: c = 0
  52.     For j = sj To m2
  53.         c = c + 1
  54.         tem(i, j) = tem(i, j) + b(r, c)
  55.         If tem(i, j) > 1 Then Exit Function
  56.     Next
  57. Next
  58. istrue = True
  59. End Function
  60. Private Function getpos(br)
  61. For i = 1 To UBound(br)
  62.     For j = 1 To UBound(br, 2)
  63.         If br(i, j) = 0 Then
  64.             getpos = Array(i, j)
  65.             Exit Function
  66.         End If
  67.     Next
  68. Next
  69. getpos = Array(0, 0)
  70. End Function
  71. Private Sub getblock(ar)
  72. For i = 1 To 49
  73.     If Cells(i, 1) <> "" Then
  74.         r = r + 1: c = 0
  75.         For j = 3 To 20
  76.             If Cells(i, j) <> "" And Cells(i, j - 1) = "" Then
  77.                 c = c + 1
  78.                 ar(r, c) = Cells(i, j).CurrentRegion
  79.             End If
  80.         Next
  81.     End If
  82. Next
  83. End Sub

复制代码

TA的精华主题

TA的得分主题

发表于 2025-8-12 22:17 | 显示全部楼层
image.jpg
给孩子小时候买过这个,拼接组合,单纯的完成组合,其实开始可以随便拼,到最后几个的时候,如果发现完不成,可以退回到前面几步,换个方案,基本上多试几次就出结果了。

不过转化为代码,除了暴力循环组合,不知道有什么好的方法。

TA的精华主题

TA的得分主题

发表于 2025-8-12 22:29 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
AI的注释
  1. ' 声明变量
  2. Dim n&, ar, spath$, rol&
  3. ' & 表示Long类型,$表示String类型
  4. ' n: 整数变量,用于定义数组大小
  5. ' ar: 变体数组,用于存储数据块
  6. ' spath: 字符串变量,用于存储路径结果
  7. ' rol: 整数变量,可能用于记录行数

  8. ' 主过程:测试程序入口
  9. Sub test2()
  10.     n = 12  ' 设置n的值为12
  11.     ReDim ar(1 To n, 1 To 4)  ' 重新定义ar数组的大小为12行4列
  12.    
  13.     getblock ar  ' 调用getblock过程,从工作表中获取数据块并存储到ar数组
  14.    
  15.     ' 重新定义结果数组res为6行10列的整数数组
  16.     ReDim res(1 To 6, 1 To 10) As Integer '6*10
  17.    
  18.     ' 重新定义布尔数组u为n个元素,用于标记数据块是否被使用
  19.     ReDim u(1 To n) As Boolean
  20.    
  21.     spath = "": rol = 0  ' 初始化路径字符串和行数变量
  22.    
  23.     ' 调用深度优先搜索(DFS)过程,寻找合适的路径
  24.     dfs res, u, "", False
  25.    
  26.     MsgBox spath  ' 显示找到的路径结果
  27. End Sub

  28. ' 深度优先搜索过程:用于寻找数据块的放置路径
  29. ' 参数说明:
  30. ' res: 结果数组,记录数据块的放置情况
  31. ' u: 布尔数组,标记数据块是否已使用
  32. ' ss: 字符串,记录当前路径
  33. ' flag: 布尔值,标记是否找到有效路径
  34. Private Sub dfs(res, u, ss, flag)
  35.     pos = getpos(res)  ' 获取结果数组中下一个需要填充的位置
  36.     x = pos(0): y = pos(1)  ' 分解位置坐标(x行, y列)
  37.    
  38.     ' 如果x为0,表示已经找到一个完整的填充方案
  39.     If x = 0 Then
  40.         '    rol = rol + 1
  41.         '    Cells(rol, "w") = ss  ' 注释:将路径写入工作表W列
  42.         spath = ss  ' 保存当前路径
  43.         flag = True  ' 设置找到路径的标记
  44.         Exit Sub  ' 退出过程
  45.     End If
  46.    
  47.     ' 遍历所有数据块
  48.     For i = 1 To n
  49.         ' 如果数据块i未被使用
  50.         If Not u(i) Then
  51.             ' 遍历数据块i的4个方向/形态
  52.             For j = 1 To 4
  53.                 b = ar(i, j)  ' 获取数据块i的第j种形态
  54.                
  55.                 ' 如果b是数组(有效的数据块)
  56.                 If IsArray(b) Then
  57.                     tem = res  ' 复制当前结果数组到临时数组
  58.                     
  59.                     ' 检查数据块b是否可以放置在(x,y)位置
  60.                     If istrue(x, y, b, tem) Then
  61.                         u(i) = True  ' 标记数据块i为已使用
  62.                         
  63.                         ' 递归调用dfs,继续寻找下一个位置
  64.                         dfs tem, u, ss & "[" & i & "," & j & "]", flag
  65.                         
  66.                         ' 如果已经找到有效路径,退出所有循环
  67.                         If flag Then Exit Sub
  68.                         
  69.                         u(i) = False  ' 回溯:取消数据块i的使用标记
  70.                     End If
  71.                 End If
  72.             Next
  73.         End If
  74.     Next
  75. End Sub

  76. ' 检查数据块是否可以放置在指定位置的函数
  77. ' 参数:
  78. ' x,y: 放置位置的坐标
  79. ' b: 要放置的数据块
  80. ' tem: 临时结果数组
  81. ' 返回值:布尔值,True表示可以放置,False表示不能放置
  82. Private Function istrue(x, y, b, tem) As Boolean
  83. Dim r&, c&, k&  ' 声明行、列、计数变量

  84. ' 计算数据块b的左侧空白格数量
  85. For i = 1 To UBound(b, 2)
  86.     If b(1, i) = 1 Then Exit For  ' 遇到第一个有效单元格时退出
  87.     k = k + 1  ' 累计空白格数量
  88. Next

  89. ' 计算放置数据块后的起始和结束坐标
  90. si = x  ' 起始行
  91. sj = y - k  ' 起始列(考虑左侧空白)
  92. m1 = x + UBound(b) - 1  ' 结束行
  93. m2 = y + UBound(b, 2) - 1 - k  ' 结束列

  94. ' 检查是否超出结果数组的边界
  95. If m1 > UBound(tem) Or m2 > UBound(tem, 2) Or sj < 1 Then Exit Function

  96. ' 检查数据块放置后是否与已有数据重叠
  97. For i = si To m1
  98.     r = r + 1: c = 0  ' 重置列计数器
  99.     For j = sj To m2
  100.         c = c + 1  ' 累加列索引
  101.         tem(i, j) = tem(i, j) + b(r, c)  ' 模拟放置数据块
  102.         
  103.         ' 如果有重叠(单元格值大于1),则不能放置
  104.         If tem(i, j) > 1 Then Exit Function
  105.     Next
  106. Next

  107. istrue = True  ' 所有检查通过,可以放置
  108. End Function

  109. ' 获取结果数组中下一个需要填充的位置(第一个值为0的单元格)
  110. Private Function getpos(br)
  111.     ' 遍历结果数组
  112.     For i = 1 To UBound(br)
  113.         For j = 1 To UBound(br, 2)
  114.             If br(i, j) = 0 Then  ' 找到值为0的单元格
  115.                 getpos = Array(i, j)  ' 返回该单元格的坐标
  116.                 Exit Function  ' 退出函数
  117.             End If
  118.         Next
  119.     Next
  120.     getpos = Array(0, 0)  ' 如果所有单元格都已填充,返回(0,0)
  121. End Function

  122. ' 从工作表中获取数据块并存储到ar数组
  123. Private Sub getblock(ar)
  124. Dim r&, c&  ' 声明行、列计数器变量,用于ar数组的索引

  125. ' 遍历工作表第1列的前49行
  126. For i = 1 To 49
  127.     ' 如果当前行第1列有数据(表示这是一个数据块的起始行)
  128.     If Cells(i, 1) <> "" Then
  129.         r = r + 1: c = 0  ' 行计数器加1,重置列计数器
  130.         
  131.         ' 遍历第3列到第20列,寻找数据块
  132.         For j = 3 To 20
  133.             ' 找到新的数据块(当前单元格有数据且前一个单元格无数据)
  134.             If Cells(i, j) <> "" And Cells(i, j - 1) = "" Then
  135.                 c = c + 1  ' 列计数器加1
  136.                
  137.                 ' 将当前单元格所在的连续数据区域作为一个数据块,存储到ar数组
  138.                 ar(r, c) = Cells(i, j).CurrentRegion
  139.             End If
  140.         Next
  141.     End If
  142. Next
  143. End Sub
复制代码

这段代码看起来是一个用于解决类似拼图问题的程序:从工作表中读取多个数据块(可能是不同形状的),然后通过深度优先搜索 (DFS) 算法尝试将这些数据块填充到一个 6×10 的网格中,最终找到一种有效的组合方式并输出路径。

代码的核心逻辑是:

从工作表读取数据块到 ar 数组
使用 DFS 算法尝试所有可能的组合方式
通过 istrue 函数检查数据块是否可以放置在指定位置
找到有效组合后,将路径存储在 spath 变量并显示

这种算法常用于解决排列组合、路径搜索类问题,通过递归和回溯的方式探索所有可能的解。

TA的精华主题

TA的得分主题

发表于 2025-8-12 23:21 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2025-8-13 09:11 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2025-8-17 10:46 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
对yynrzwh老师分享的示例文件做了一些小修改。
1、增加的块的其他姿态的排列组合;
2、增加了5*12的排列计算;
3、对十二块的组合计算做了乱序处理(xr()),可使得每次结果都不同。

再次感谢老师的指导和帮助,运行速度稍慢。能否请老师看看能否优化。

十二块凑长方形testa.7z

32.81 KB, 下载次数: 8

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2025-8-17 10:49 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2025-8-19 09:22 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
henterwu 发表于 2025-8-17 10:46
对yynrzwh老师分享的示例文件做了一些小修改。
1、增加的块的其他姿态的排列组合;
2、增加了5*12的排列 ...

图形越多越慢。
BFS,DFS都是遍历,所以很慢,效率低。
要快就要剪枝,或者换其他算法。
目前没想到其他办法。

评分

1

查看全部评分

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2025-12-7 12:44 , Processed in 0.028250 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表