铺瓷砖问题

这是最近学离散数学中的一个问题

不在这里引用问题的官方介绍了,简单地说,就是左图这样的MxN的地板,用右图这样的瓷砖来铺,可对右图的瓷砖进行旋转和翻折。(当然这个问题有很多有趣的形式,不具体讨论,这不是重点

问题的解决

首先有很多有趣的数学方法来解决这个问题,不详细叙述。我想用最笨的方法(当然最终也没能用这种方法解决,但是总归有些收获),逐一的去尝试各种可能情况来解决问题。当然这样的尝试只能靠计算机来实现了。

寻找解决思路

首先要找到一种方法来表示这样的地板。
表示这样的地板需要表示MxN个数据,以及各个数据之间的位置关系。

若只描述数据的话,数组是一个很好的选择,但是数组在表示他们之间的位置关系时略显乏力。可以通过特定的函数来按照位置关系来对其进行操作,但是这样并不能使数据本身包含位置关系的特性。

考虑到链表。通常的链表对应一维数组,但是对其进行稍稍变形也许就能对应二维数组了。决定采用这样的方式来表示数据。

类设计

多次试错后,做出一个我觉得值得记录的方法(没学过数据结构,可能设计不是很好,不好又咋滴,我高兴

本打算将格点设计为一个类,再通过has-a继承得到瓷砖的类。然而发现不同格点的几何关系有较大差异,这样的设计貌似有点困难。
索性直接来设计整个瓷砖的类(接下来用用Block表示),不同的瓷砖的几何关系通过不同构造函数的行为来描述。这就需要设计构造函数。为了设计构造函数,需要首先对这个数据的特性分析,并提炼其特性。

图乱七八糟,希望我能说明白。
从第一个点开始生成整个表格。首先需要构造第一个格点(1,1),其对应构造函数f1。
在f1中需要向左向右分别构造两个格点。在新的格点中需要继续向左向右构造格点。

如上图,以这样的太极生两仪,两仪生四象,四象生八卦的方式来使得格点扩张。然而有一个问题,两亿生四象之后,有两象实际上是同一象,这就需要想办法使其合并。

如上图,一个不错的方法是使得两仪有不同的行为。其中一个仪生两象,而另一个仪生一象,同时使用另一仪的一象。
但是还有问题,这样的方法是按照三角的形式扩张的,如何使其结束并成为矩形?一个方法是将三角形削掉两角变成矩形。

如上图,令三象(四象并为三象)的一象所生的一卦为虚无,虚无只能再生虚无,也就是削掉了三角形的角。

这是整体的思想,其中的太极,两仪,八卦均为格点,也就是数据。“生”的过程其实就是构造的过程,生成一个虚无,即为指向一个空指针。经过这样的提炼,得到了一个应该可行的设计思路

.可以看到,格点有两种不同的行为,一种是生成两个格点,另一种是生成一个格点并使用一个格点。这就要求有两种不同的构造函数
.为了实现连续的生成,可以想到,我们会在构造函数中调用构造函数
.但是这样的递归是无限的,如何使其停下来,我们需要条件判断
.如何设计判断,需要记录向左或向右生成的格点个数,当其到达m或n是停下来。这样的记录数据应该是属于类的,所以需要静态变量
.在构造函数前要设置格点总数,也就是m和n,这样属于类的特性,需要静态函数和静态变量实现,而且需要在构造前调用。

类的C++实现

成员变量

class Block
{
private://辅助记录格点位置static int leftnum ;static int rightnum; //记录格点位置int x;int y;//记录表格大小static int row;static int column;//记录格点数static int num;//记录格点状态bool cover;//描述位置关系Block* left;Block* right;
}

为了描述两种生成格点方式,使用两种构造函数

public://两种构造函数Block();Block(Block* right);

其实现稍后介绍

要在建立对象之前设置地板大小

static bool setbock(int r, int c);

其实现较为简单

bool Block::setbock(int r, int c)
{Block:: row = r;Block::column = c;if (r || c)return true;return false;
}

当然要有析构函数

~Block();

构造函数实现

构造函数是最重要的部分,介绍其实现

根据这张图来完成设计
咖啡色三点有相同的行为,均生成两格点。虽然最右边的那个格点生成的两个格点之一为空指针。其特点为,每个格点都是上一个个点向右生成的。首先完成这个构造。

Block::Block() :left(nullptr), right(nullptr)
{//每次构造,格点数加一num++;std::cout << num << "-----" << std::endl;cover = false;//先忽略以下三行x = rightnum;y = leftnum;std::cout <<x<<y<< std::endl;//若未到达边界,向右构造if (rightnum < row){rightnum++;//向右构造所得格点任然遵循这个构造函数的行为right = new Block();}//若未到达边界,向左构造if (leftnum < column){leftnum++;//向左构造得到格点不再符合这个构造函数//使用另一个构造函数,先去下一部分研究另一个构造函数if (right)left = new Block(right->left);elseleft = new Block(nullptr);}
}

接下来是另一个构造函数,是上图绿色个点所使用的构造方法。
由于在构造时要使用一个已有的格点(这也是为什么先从右开始构造的原因),所以需要一个参数

Block::Block(Block* right)
{num++;std::cout << num<<"-----" << std::endl;cover = false;//忽略以下三行x = rightnum;y = leftnum;std::cout << x << y << std::endl;if (rightnum < row){//向右构造时使用一个已有格点,这个格点通过参数传入,即right,将传入个right赋值给this的right变量,完成了对已有格点的使用    this->right = right;}if (leftnum < column){//向左构造时,任然生成绿色格点,所使用构造方法与此构造方法相同,递归即可leftnum++;//考虑边际格点指向nullptr后不能对nullptr操作,需要分情况if (right)//如果未到达边界,直接递归//根据几何关系,传入的参量(即所使用的已有格点)为刚才向右使用的格点的左指向格点。//也就是this->left->right=this->right->left,省略this,right->即为传入参数left = new Block(right->left);else//如果已经到达边界,使用的格点为nullptrleft = new Block(nullptr);}//先忽略以下三行leftnum--;if (leftnum == 1)rightnum --;
}

回到第一个构造函数中的向左构造,即棕色格点的向左构造得到绿色格点

 if (right)//绿色格点向右使用一个已有格点,根据几何关系,所使用的这个格点正是刚才向右构造的格点(this->right) 生成的左格点(this->right)->left   //即this->left->right=this->right->leftleft = new Block(right->left);elseleft = new Block(nullptr);

完成了整体的构造框架,但是还有细节需要考虑
如果只有刚才所涉及的步骤,无法完成所有的过程,因为记录向左或向右构造的格点的个数的变量rightnum和leftnum在到达边界后未进行修正,使得只能进行一次完全递增。

就像是这样,无法完整的构造。

我们知道迭代的函数在遇到边际然后退出时,会依次执行剩余的步骤,用此方法,在退出所执行的代码中加入修正,依下图所示

1234步骤是刚才的构造过程,567为修正过程,89构造,10、11、12修正。。。按照这种方法,使得rightnum和leftnum所表示的值总为当前格点所在位置。

leftnum--;
if (leftnum == 1)rightnum --;

这三行完成的便是修正的任务。只有在构造函数2执行到边际时,也就是菱形图案的最下面点是,迭代开始结束,开始依次执行每次构造2的这段代码,便完成了修正。

正如提到的,rightnum和leftnum所表示的值总为当前格点所在位置。所以可以用属于对象的变量x,y来记录每个对象的位置,并输出检验。当然这是可有可无的。

 x = rightnum;y = leftnum;std::cout << x << y << std::endl;

由于在构造是使用了new,一定要在析构时将其分配的内存释放。

Block::~Block()
{delete left;delete right;
}

析构函数完成了这些工作。析构和构造顺序相反使得析构可以由下到上顺利完成。

进行验证

 Block::setbock(3, 3);Block A;

输出结果如下

1-----
11
2-----
21
3-----
31
4-----
32
5-----
33
6-----
22
7-----
23
8-----
12
9-----
13

这与我们设想的构造顺序是一致的。

what a pity

然而用这种方法建立的数据去解决问题的时候,发现并没有简单。
因为是想用遍历所有情况的笨办法来解决问题,考虑了两种方法。
一种为有一定顺序的先左后右的方法来遍历,然而失败了,我都想不明白一共要进行几次铺砖。
另一种为用随机数来决定每一步的走法,然后不断循环进行。然而这种方法并不能保证解决问题,只能是一定概率可解决问题。
是我太菜了。。。

思考

对建立地板模型的过程有些想法。

用链表的思想可以建立数据之间的几何关系。但是只描述了单项几何关系。
如果使用双向链表便可以建立完整的一块地板。(当然我觉得在这个问题中不需要双向链表)。

如果是一个扫地机器人,一次可以清理一块地板。是不是可以利用这种方法来描述扫地机器人对地板的识别,然后使用随机的方法来让扫地机器人进行清理,并记录时间。挑选时间较短的实验进行研究,说不定可以发现很好的控制扫地机器人路径选择的算法。
当然只是自己想想而已。毕竟还是大白菜,还有好多需要学。

如果建立一个二维指针数组来记录每一格点,那样有很方便索引了。虽然我还没找到一个好方法完成这件事。又或许这种方法本事就有问题。。。。

菜是菜了点,菜的不止一点
还是要厚着脸皮推荐一下小白猿的公众号

要不干脆叫大白菜得了。。。

铺瓷砖问题的C++实现相关推荐

  1. python递归编程题_Python数据结构与算法41:递归编程练习题4:铺瓷砖

    注:本文如涉及到代码,均经过Python 3.7实际运行检验,保证其严谨性. 本文阅读时间约为8分钟. 递归编程练习题4:铺瓷砖 给定一个长度为N的区域,及4种不同长度的瓷砖:灰瓷砖(长为1格).红瓷 ...

  2. 第4-5课:铺瓷砖问题

    铺瓷砖.铺地板.在电路板上嵌入芯片等问题,都属于一类问题,基本上可以描述为在一个 N × M 的平面空间中摆放一些形状固定的物品,要求覆盖整个平面空间,问有多少种摆放方法.在某些情况下还会增加一点难度 ...

  3. 15年国赛 铺瓷砖

    标题:铺瓷砖 为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖.机房可以看成一个n*m的矩形,而这次使用的瓷砖比较特别,有两种形状,如[图1.png]所示.在铺放瓷砖时,可以旋转.   ...

  4. LeetCode 1240. 铺瓷砖(深搜剪枝)

    1240. 铺瓷砖 你是一位施工队的工长,根据设计师的要求准备为一套设计风格独特的房子进行室内装修. 房子的客厅大小为 n x m,为保持极简的风格,需要使用尽可能少的 正方形 瓷砖来铺盖地面. 假设 ...

  5. Java B组蓝桥杯第六届国赛:铺瓷砖

    这101分的题真的搞不定........ 本人能力有限,搜遍百度,没有找到完美的代码..... 倒是找到一堆文章在写多米诺骨牌版的铺地砖,但那些文章内容很杂,不知所云,无法耐心研究下去,但也许能类比到 ...

  6. yzm10铺瓷砖 yzm10原创系列

    yzm10铺瓷砖 一天yzm10接到任务,要求用2×1大小的瓷砖,来铺2×4的地面,地面需要恰好被铺满.这对yzm10来说太容易了,于是他马上设计出了5种不同的铺法(旋转情况算不同种,如图示2.4). ...

  7. yzm10铺瓷砖 一只小蜜蜂 ycb与取款机

    yzm10铺瓷砖 一天yzm10接到任务,要求用2×1大小的瓷砖,来铺2×4的地面,地面需要恰好被铺满.这对yzm10来说太容易了,于是他马上设计出了5种不同的铺法(旋转情况算不同种,如图示2.4). ...

  8. 【每日一题Day231】LC1240铺瓷砖 | 暴力回溯

    铺瓷砖[LC1240] 你是一位施工队的工长,根据设计师的要求准备为一套设计风格独特的房子进行室内装修. 房子的客厅大小为 n x m,为保持极简的风格,需要使用尽可能少的 正方形 瓷砖来铺盖地面. ...

  9. 铺瓷砖问题 (状态压缩动态规划) (一)

    作者: Phill King 邮箱: phillking1982@163.com 原创文章,转载请注明出处. 题目地址:2411 -- Mondriaan's Dream 问题简单描述: 在一个N行M ...

  10. 蓝桥杯C++B组2017决赛铺瓷砖

    参考自蓝桥杯国赛特训营 题意 有一个 3×10 的空地,往里面铺 1×2 的瓷砖,瓷砖有两种颜色(橙色和黄色),并且任意一个 2×2 的空地的瓷砖颜色不能相同,问共有几种贴法. 分析 数据范围很小,只 ...

最新文章

  1. 容器化方案Docker的使用方法
  2. Nginx 教程:基本概念
  3. pycharm 如何使用git链接到github实现代码提交
  4. Mysql 基础语法
  5. 红帽linux进阶,Linux进阶第二天
  6. 结构光相移法-多频外差原理+实践(上)
  7. 计算机学院篮球赛主题,计算机学院称雄中国科大2011年学生篮球赛
  8. 3S技术集成与综合应用实习报告(一)
  9. 去伪、存真、打破、重塑……网贷业暴雷潮之下的敬畏与回归
  10. 基于mysql+php的英语四六级过级成绩管理
  11. COSCon'22 第七届中国开源年会火热报名中,喜迎新一波赞助伙伴
  12. 第十三课 类族结构的进化
  13. 互联网金融的前世、今生和未来-系列四(今生):百花齐放的互联网金融业态
  14. Unity 负无穷 正无穷
  15. spring integration-概述
  16. BAT互联网公司的程序员,应用程序架构思路,这些都懂?
  17. 简单的S40后台运行
  18. 解决UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xfd in position 1527802: illegal multibyte sequ
  19. 【老王的脑科学谬论】在CSDN问答区对网友提问的回复(一)
  20. RMD kint 不输出 include=TRUE,echo=TRUE

热门文章

  1. 三个优秀的Android图表开源控件
  2. 阿里规范说MySQL单表行数不要超过2000w,为啥?
  3. c语言令牌桶原理,令牌桶算法的使用
  4. python利用PyQt5制作QQ空间登入客户端
  5. 如何用DETR(detection transformer)训练自己的数据集
  6. 麒麟V10系统-软件商店更新源问题
  7. word文件转pdf转换器11.0注册码
  8. python装饰器详解
  9. 台达服务器电源原理电路图,详细解析开关电源电路:工作原理,电路组成,电路图...
  10. 从零开始的JAVA反序列化漏洞学习(一)