铺瓷砖问题的C++实现
铺瓷砖问题
这是最近学离散数学中的一个问题
不在这里引用问题的官方介绍了,简单地说,就是左图这样的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++实现相关推荐
- python递归编程题_Python数据结构与算法41:递归编程练习题4:铺瓷砖
注:本文如涉及到代码,均经过Python 3.7实际运行检验,保证其严谨性. 本文阅读时间约为8分钟. 递归编程练习题4:铺瓷砖 给定一个长度为N的区域,及4种不同长度的瓷砖:灰瓷砖(长为1格).红瓷 ...
- 第4-5课:铺瓷砖问题
铺瓷砖.铺地板.在电路板上嵌入芯片等问题,都属于一类问题,基本上可以描述为在一个 N × M 的平面空间中摆放一些形状固定的物品,要求覆盖整个平面空间,问有多少种摆放方法.在某些情况下还会增加一点难度 ...
- 15年国赛 铺瓷砖
标题:铺瓷砖 为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖.机房可以看成一个n*m的矩形,而这次使用的瓷砖比较特别,有两种形状,如[图1.png]所示.在铺放瓷砖时,可以旋转. ...
- LeetCode 1240. 铺瓷砖(深搜剪枝)
1240. 铺瓷砖 你是一位施工队的工长,根据设计师的要求准备为一套设计风格独特的房子进行室内装修. 房子的客厅大小为 n x m,为保持极简的风格,需要使用尽可能少的 正方形 瓷砖来铺盖地面. 假设 ...
- Java B组蓝桥杯第六届国赛:铺瓷砖
这101分的题真的搞不定........ 本人能力有限,搜遍百度,没有找到完美的代码..... 倒是找到一堆文章在写多米诺骨牌版的铺地砖,但那些文章内容很杂,不知所云,无法耐心研究下去,但也许能类比到 ...
- yzm10铺瓷砖 yzm10原创系列
yzm10铺瓷砖 一天yzm10接到任务,要求用2×1大小的瓷砖,来铺2×4的地面,地面需要恰好被铺满.这对yzm10来说太容易了,于是他马上设计出了5种不同的铺法(旋转情况算不同种,如图示2.4). ...
- yzm10铺瓷砖 一只小蜜蜂 ycb与取款机
yzm10铺瓷砖 一天yzm10接到任务,要求用2×1大小的瓷砖,来铺2×4的地面,地面需要恰好被铺满.这对yzm10来说太容易了,于是他马上设计出了5种不同的铺法(旋转情况算不同种,如图示2.4). ...
- 【每日一题Day231】LC1240铺瓷砖 | 暴力回溯
铺瓷砖[LC1240] 你是一位施工队的工长,根据设计师的要求准备为一套设计风格独特的房子进行室内装修. 房子的客厅大小为 n x m,为保持极简的风格,需要使用尽可能少的 正方形 瓷砖来铺盖地面. ...
- 铺瓷砖问题 (状态压缩动态规划) (一)
作者: Phill King 邮箱: phillking1982@163.com 原创文章,转载请注明出处. 题目地址:2411 -- Mondriaan's Dream 问题简单描述: 在一个N行M ...
- 蓝桥杯C++B组2017决赛铺瓷砖
参考自蓝桥杯国赛特训营 题意 有一个 3×10 的空地,往里面铺 1×2 的瓷砖,瓷砖有两种颜色(橙色和黄色),并且任意一个 2×2 的空地的瓷砖颜色不能相同,问共有几种贴法. 分析 数据范围很小,只 ...
最新文章
- 容器化方案Docker的使用方法
- Nginx 教程:基本概念
- pycharm 如何使用git链接到github实现代码提交
- Mysql 基础语法
- 红帽linux进阶,Linux进阶第二天
- 结构光相移法-多频外差原理+实践(上)
- 计算机学院篮球赛主题,计算机学院称雄中国科大2011年学生篮球赛
- 3S技术集成与综合应用实习报告(一)
- 去伪、存真、打破、重塑……网贷业暴雷潮之下的敬畏与回归
- 基于mysql+php的英语四六级过级成绩管理
- COSCon'22 第七届中国开源年会火热报名中,喜迎新一波赞助伙伴
- 第十三课 类族结构的进化
- 互联网金融的前世、今生和未来-系列四(今生):百花齐放的互联网金融业态
- Unity 负无穷 正无穷
- spring integration-概述
- BAT互联网公司的程序员,应用程序架构思路,这些都懂?
- 简单的S40后台运行
- 解决UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xfd in position 1527802: illegal multibyte sequ
- 【老王的脑科学谬论】在CSDN问答区对网友提问的回复(一)
- RMD kint 不输出 include=TRUE,echo=TRUE