C++入门教程(23)深度优先,广度优先(连连看判断是否可以消除)
最近看一个人实现连连看是否可以消除的核心代码,几分钟就写完了,手痒痒自己写了一遍
需求:实现一个函数,输入两个选中的块,判断是否可以消除
连连看规则,两个相同的块可以消除,但是需要满足以下条件,
两个块的连线上不能有其他块
连线的转折次数不能超过2
处在边界的两个块可以绕出地图一个格子去连线
首先是实现连连看的地图,本例使用的是10X10的地图,边界都为0,真实的块只有8X8
int map[MaxLen][MaxHeight] = {0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,1,1,0,0,1,3,1,3,1,1,0,0,0,0,4,0,1,0,1,1,0,0,0,0,1,0,1,0,1,0,0,0,0,0,4,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,5,1,1,0,1,0,0,0,0,0,1,5,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
0表示可以连线,1是测试的不可连线的值,3,4,5为三对目标块,用来判断是否可以删除
这个程序有几个难点,一个是如何搜索下一个节点,一个是如何避免重复搜索,还有就是如何得到已经找到的路径(需要进行显示线)
搜索下一个节点的实现
由于连线只能是相邻的两个块去连接,所以任意一个目标块为起点,按照四个方向去寻找可搜索的块,直到找到目标块为止。
那么需要定义四个方向,上下左右,
int dir_x[4] = { -1, 1, 0, 0 };
int dir_y[4] = { 0, 0, -1, 1 };
这里用0-3来表示4个方向,每个节点下标和每一个方向对应的dir_x和dir_y进行相加,即可得到相应方向的下一个节点
比如节点 (x1,y1) 遍历4个方向可得到四个新的坐标(x1-1,y1) ,(x1+1,y1) ,(x1,y1-1) ,(x1,y1+1)
避免重复查找
在编码的过程中会发现下面的情况会产生闭环,导致死循环一直查找下去
0,0
0,00,0,0,
0,1,0,
0,0,0,
找了一圈发现又回到第一个0搜索,所以需要实现不能有三次转折,也就是十字路口左转,十字路口左转,十字路口左转,三个红路灯以后,你就能再次看到给你指路的那个大聪明了。
//当前节点的查找方向int dir;//经过几次转弯int turn;
为了判断是否有转折需要在块中定义每个块的搜索方向,搜索到当前块经过了几次的转折,只要转折超过3次,非法搜索,这样可以防止闭环的产生。
得到已经找到的路径
为了得到已经找到的路径,需要逆向找到前面的节点,所以节点需要定义一个父节点,用来回退找到前面的所有节点。在实现的过程中,没移动一个方向,就制定当前节点为下个节点的parent
块的定义
class Tile
{
public://坐标int x, y;//图片值: 0表示已消除 ,大于0表示图片的idxint value;//当前节点的查找方向int dir;//经过几次转弯int turn;//查找深度int depth;Tile * parent;void init(){dir = -1;turn = -1;depth = -1;parent = NULL;}};
深度优先搜索实现:
//深度优先
bool DFS(Tile tiles[MaxLen][MaxHeight],Tile t1, Tile t2)
{//两个相同节点不可if (t1.x == t2.x && t1.y == t2.y){return false;}//坐标非法不可if (t1.x < 0|| t2.x < 0|| t1.y>= MaxHeight || t2.y>= MaxHeight|| t1.x >= MaxLen || t2.x >= MaxLen){return false;}//深度优先搜索来从一个点找到另一个点Tile * startTile = &t1;startTile->depth = 0;stack<Tile*> sta;sta.push(startTile);vector<Tile * > paths;int result = 0;//开始节点t1一直向目标节点t2移动 能重合则结束while (sta.size() > 0){Tile* lastTile = sta.top();sta.pop();//尝试寻找for (int i = 0; i< 4 ; i++){int x = lastTile->x + dir_x[i];int y = lastTile->y + dir_y[i];if (x<0 || x >= MaxLen || y < 0 || y>= MaxHeight){continue;}Tile *curTile = &tiles[x][y];//上一个节点转了几次弯curTile->turn = lastTile->turn;curTile->dir = i;curTile->depth = lastTile->depth + 1;//如果上一个节点方向改变if (lastTile->dir != curTile->dir){curTile->turn++;}//检查是否回退回去if(dir_x[lastTile->dir] + dir_x[curTile->dir] == 0 && dir_y[lastTile->dir] + dir_y[curTile->dir] == 0){//cout << " 不可以回退访问" << endl;continue;}//超过两次转弯不合法if (curTile->turn > 2){continue;}//找到相邻的合法节点if (curTile->value == 0){//cout << " 插入节点x " << curTile->x << " y " << curTile->y << endl;//cout<< " 插入节点转弯次数"<< curTile->turn<< endl;sta.push(curTile);curTile->parent = lastTile;}else if(curTile->value == startTile->value && (curTile->x != startTile->x || curTile->y != startTile->y)){curTile->parent = lastTile;cout << " 找到一条存储路径 搜索深度:" << curTile->depth<< endl;while (curTile->parent != NULL) {cout<<"路径坐标 x = "<< curTile->parent->x<< ",y = "<< curTile->parent->y << endl;curTile = curTile->parent;}result ++;//return true;}}}//return false;return result > 0;
}
由于深度优先搜索第一次查找到的不一定是最优解,所以需要将所有的结果都找到,并从中选取一个最优的结果。本文并没有实现,只是找到了所有的情况,读者可以自行实现,将所有的结果存在vector中,选取最少的那一条即可
广度优先搜索实现:
bool BFS(Tile tiles[MaxLen][MaxHeight],Tile t1, Tile t2)
{//两个相同节点不可if (t1.x == t2.x && t1.y == t2.y){return false;}//坐标非法不可if (t1.x < 0|| t2.x < 0|| t1.y>= MaxHeight || t2.y>= MaxHeight|| t1.x >= MaxLen || t2.x >= MaxLen){return false;}//深度优先搜索来从一个点找到另一个点Tile * startTile = &t1;startTile->depth = 0;queue<Tile*> que;que.push(startTile);vector<Tile * > paths;//开始节点t1一直向目标节点t2移动 能重合则结束while (que.size() > 0){Tile* lastTile = que.front();que.pop();//尝试寻找for (int i = 0; i< 4 ; i++){int x = lastTile->x + dir_x[i];int y = lastTile->y + dir_y[i];if (x<0 || x >= MaxLen || y < 0 || y>= MaxHeight){continue;}Tile *curTile = &tiles[x][y];//上一个节点转了几次弯curTile->turn = lastTile->turn;curTile->dir = i;curTile->depth = lastTile->depth + 1;//如果上一个节点方向改变if (lastTile->dir != curTile->dir){curTile->turn++;}//检查是否回退回去if(dir_x[lastTile->dir] + dir_x[curTile->dir] == 0 && dir_y[lastTile->dir] + dir_y[curTile->dir] == 0){//cout << " 不可以回退访问" << endl;continue;}//超过两次转弯不合法if (curTile->turn > 2){continue;}//找到相邻的合法节点if (curTile->value == 0){//cout << " 插入节点x " << curTile->x << " y " << curTile->y << endl;//cout<< " 插入节点转弯次数"<< curTile->turn<< endl;que.push(curTile);curTile->parent = lastTile;}else if(curTile->value == startTile->value && (curTile->x != startTile->x || curTile->y != startTile->y)){curTile->parent = lastTile;cout << " 找到一条存储路径 搜索深度:" << curTile->depth<< endl;while (curTile->parent != NULL) {cout<<"路径坐标 x = "<< curTile->parent->x<< ",y = "<< curTile->parent->y << endl;curTile = curTile->parent;}return true;}}}return false;}
由于广度优先搜索第一个得到的肯定是最优解。所以这个函数找到后就直接返回了。
其实深度优先和广度优先搜索区别只是栈和队列的区别,深度使用的是栈后进先出,广度使用的是队列,先进先出。突然觉得这个栈的实现会不会是为了方便回退到上一个节点而产生的数据结构。
完整代码:
#include <iostream>
#include <vector>
#include <stack>
#include <queue>using namespace std;
#define MaxLen 10
#define MaxHeight 10// 上下左右位移 上,下,左,右
int dir_x[4] = { -1, 1, 0, 0 };
int dir_y[4] = { 0, 0, -1, 1 };//为了方便计算边界可以消除 , 地图数据(最外面一圈都是0) 真实的砖块所在区域为内部8X8的区域
int map[MaxLen][MaxHeight] = {0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,1,1,0,0,1,3,1,3,1,1,0,0,0,0,4,0,1,0,1,1,0,0,0,0,1,0,1,0,1,0,0,0,0,0,4,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,5,1,1,0,1,0,0,0,0,0,1,5,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};class Tile
{
public://坐标int x, y;//图片值: 0表示已消除 ,大于0表示图片的idxint value;//当前节点的查找方向int dir;//经过几次转弯int turn;//查找深度int depth;Tile * parent;void init(){dir = -1;turn = -1;depth = -1;parent = NULL;}};//每次查询完需要重置每个块的状态 这里为了方便直接使用Tile节点,也可以自定义新的搜索节点
void initAllTiles(Tile tiles[MaxLen][MaxHeight])
{for (int i = 0; i < MaxLen; i++){for (int j = 0; j < MaxHeight; j++){Tile * t = &tiles[i][j];t->init();}}}
//深度优先
bool DFS(Tile tiles[MaxLen][MaxHeight],Tile t1, Tile t2)
{//两个相同节点不可if (t1.x == t2.x && t1.y == t2.y){return false;}//坐标非法不可if (t1.x < 0|| t2.x < 0|| t1.y>= MaxHeight || t2.y>= MaxHeight|| t1.x >= MaxLen || t2.x >= MaxLen){return false;}//深度优先搜索来从一个点找到另一个点Tile * startTile = &t1;startTile->depth = 0;stack<Tile*> sta;sta.push(startTile);vector<Tile * > paths;int result = 0;//开始节点t1一直向目标节点t2移动 能重合则结束while (sta.size() > 0){Tile* lastTile = sta.top();sta.pop();//尝试寻找for (int i = 0; i< 4 ; i++){int x = lastTile->x + dir_x[i];int y = lastTile->y + dir_y[i];if (x<0 || x >= MaxLen || y < 0 || y>= MaxHeight){continue;}Tile *curTile = &tiles[x][y];//上一个节点转了几次弯curTile->turn = lastTile->turn;curTile->dir = i;curTile->depth = lastTile->depth + 1;//如果上一个节点方向改变if (lastTile->dir != curTile->dir){curTile->turn++;}//检查是否回退回去if(dir_x[lastTile->dir] + dir_x[curTile->dir] == 0 && dir_y[lastTile->dir] + dir_y[curTile->dir] == 0){//cout << " 不可以回退访问" << endl;continue;}//超过两次转弯不合法if (curTile->turn > 2){continue;}//找到相邻的合法节点if (curTile->value == 0){//cout << " 插入节点x " << curTile->x << " y " << curTile->y << endl;//cout<< " 插入节点转弯次数"<< curTile->turn<< endl;sta.push(curTile);curTile->parent = lastTile;}else if(curTile->value == startTile->value && (curTile->x != startTile->x || curTile->y != startTile->y)){curTile->parent = lastTile;cout << " 找到一条存储路径 搜索深度:" << curTile->depth<< endl;while (curTile->parent != NULL) {cout<<"路径坐标 x = "<< curTile->parent->x<< ",y = "<< curTile->parent->y << endl;curTile = curTile->parent;}result ++;//return true;}}}//return false;return result > 0;
}bool BFS(Tile tiles[MaxLen][MaxHeight],Tile t1, Tile t2)
{//两个相同节点不可if (t1.x == t2.x && t1.y == t2.y){return false;}//坐标非法不可if (t1.x < 0|| t2.x < 0|| t1.y>= MaxHeight || t2.y>= MaxHeight|| t1.x >= MaxLen || t2.x >= MaxLen){return false;}//深度优先搜索来从一个点找到另一个点Tile * startTile = &t1;startTile->depth = 0;queue<Tile*> que;que.push(startTile);vector<Tile * > paths;//开始节点t1一直向目标节点t2移动 能重合则结束while (que.size() > 0){Tile* lastTile = que.front();que.pop();//尝试寻找for (int i = 0; i< 4 ; i++){int x = lastTile->x + dir_x[i];int y = lastTile->y + dir_y[i];if (x<0 || x >= MaxLen || y < 0 || y>= MaxHeight){continue;}Tile *curTile = &tiles[x][y];//上一个节点转了几次弯curTile->turn = lastTile->turn;curTile->dir = i;curTile->depth = lastTile->depth + 1;//如果上一个节点方向改变if (lastTile->dir != curTile->dir){curTile->turn++;}//检查是否回退回去if(dir_x[lastTile->dir] + dir_x[curTile->dir] == 0 && dir_y[lastTile->dir] + dir_y[curTile->dir] == 0){//cout << " 不可以回退访问" << endl;continue;}//超过两次转弯不合法if (curTile->turn > 2){continue;}//找到相邻的合法节点if (curTile->value == 0){//cout << " 插入节点x " << curTile->x << " y " << curTile->y << endl;//cout<< " 插入节点转弯次数"<< curTile->turn<< endl;que.push(curTile);curTile->parent = lastTile;}else if(curTile->value == startTile->value && (curTile->x != startTile->x || curTile->y != startTile->y)){curTile->parent = lastTile;cout << " 找到一条存储路径 搜索深度:" << curTile->depth<< endl;while (curTile->parent != NULL) {cout<<"路径坐标 x = "<< curTile->parent->x<< ",y = "<< curTile->parent->y << endl;curTile = curTile->parent;}return true;}}}return false;}int main()
{//游戏内的所有砖块Tile tiles[MaxLen][MaxHeight];//构建地图for (int i = 0; i< MaxLen ; i++){for (int j = 0; j < MaxHeight; j++){tiles[i][j].init();tiles[i][j].x = i;tiles[i][j].y = j;tiles[i][j].value = map[i][j];}}//深度优先第一个得到的不一定是最优解if (DFS(tiles, tiles[2][2], tiles[2][4])){std::cout << "3可以消除\n";}else{std::cout << "3不可以消除\n";}initAllTiles(tiles);
// for (int i = 0; i < MaxLen; i++)
// {
// for (int j = 0; j < MaxHeight; j++)
// {
// Tile * t = &tiles[i][j];
// t->init();
// }
// }//宽度优先可以第一次得到最优解if (BFS(tiles, tiles[2][2], tiles[2][4])){std::cout << "3可以消除\n";}else{std::cout << "3不可以消除\n";}initAllTiles(tiles);if (BFS(tiles, tiles[3][1], tiles[5][1])){std::cout << "4可以消除\n";}else{std::cout << "4不可以消除\n";}initAllTiles(tiles);if (BFS(tiles, tiles[7][1], tiles[8][2])){std::cout << "5可以消除\n";}else{std::cout << "5不可以消除\n";}
}
运行:
找到一条存储路径 搜索深度:10
路径坐标 x = 3,y = 4
路径坐标 x = 4,y = 4
路径坐标 x = 5,y = 4
路径坐标 x = 6,y = 4
路径坐标 x = 6,y = 3
路径坐标 x = 6,y = 2
路径坐标 x = 5,y = 2
路径坐标 x = 4,y = 2
路径坐标 x = 3,y = 2
路径坐标 x = 2,y = 2找到一条存储路径 搜索深度:6
路径坐标 x = 1,y = 4
路径坐标 x = 0,y = 4
路径坐标 x = 0,y = 3
路径坐标 x = 0,y = 2
路径坐标 x = 1,y = 2
路径坐标 x = 2,y = 2
3可以消除找到一条存储路径 搜索深度:6
路径坐标 x = 1,y = 4
路径坐标 x = 0,y = 4
路径坐标 x = 0,y = 3
路径坐标 x = 0,y = 2
路径坐标 x = 1,y = 2
路径坐标 x = 2,y = 2
3可以消除找到一条存储路径 搜索深度:4
路径坐标 x = 5,y = 0
路径坐标 x = 4,y = 0
路径坐标 x = 3,y = 0
路径坐标 x = 3,y = 1
4可以消除
5不可以消除
本例只是将搜索到的路径打印了出来,可以存在vector数组,用于返回数据
3有两个可以消除的路径
4左侧边界可以消除
5经过边界,由于需要3次转折,所以不能消除。
纯手撸还是需要点时间的,面试如果出这种题,不知道当场能不能直接写出来。继续加油吧
有个疑问,既然广度优先可以直接找到最优解,那么深度优先的优势是什么呢?有存在的必要么?难道是数量级太大的时候,广度优先所查找的范围太大导致内存吃不消么?
C++入门教程(23)深度优先,广度优先(连连看判断是否可以消除)相关推荐
- Node.js 入门教程 23 使用 npm 的语义版本控制 24 卸载 npm 软件包 25 npm 全局或本地的软件包
Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录 Node.js 入门教程 23 使用 npm 的语义版本控制 24 ...
- python判断语句入门教程_Python中的条件判断语句基础学习教程
if语句用来检验一个条件, 如果 条件为真,我们运行一块语句(称为 if-块 ), 否则 我们处理另外一块语句(称为 else-块 ). else 从句是可选的. 使用if语句: ? 输出: ? 在这 ...
- 零基础HTML入门教程(23)--HTML综合实例
点此查看 所有教程.项目.源码导航 本文目录 1. 背景 2. 开发流程 2.1 网站功能设计 2.2 建立网站目录结构 2.3 开发首页 2.2 生平简介页 2.3 经典诗词页 2.4 苏轼图集页 ...
- TensorFlow入门教程(23)将图像超分辨率模型SRGAN移植到安卓APP(下)
# #作者:韦访 #博客:https://blog.csdn.net/rookie_wei #微信:1007895847 #添加微信的备注一下是CSDN的 #欢迎大家一起学习 # 1.概述 上一讲我们 ...
- arcgis python 教程-终于晓得arcgis-python入门教程
比较操作符就是小学常常用到的,比如大于,小于,等于这些,与身份操作符不一样的是,身份操作符是对内存地址进行比较,而这个是对值进行比较比较结果要么是真(True),要么是假(Flase).以下是小编为你 ...
- python in arcgis_终于晓得arcgis-python入门教程
比较操作符就是小学常常用到的,比如大于,小于,等于这些,与身份操作符不一样的是,身份操作符是对内存地址进行比较,而这个是对值进行比较比较结果要么是真(True),要么是假(Flase).以下是小编为你 ...
- 微服务和VUE入门教程(26): 微服务之turbine
微服务和VUE入门教程(26): 微服务之turbine 微服务和VUE入门教程(0): 着手搭建项目 微服务和VUE入门教程(1): 搭建前端登录界面 微服务和VUE入门教程(2): 注册中心 微服 ...
- 零基础Bootstrap入门教程(0)--教程背景与目录
点此查看 所有教程.项目.源码导航 本文目录 1. 背景 2. 教程目录 3. 源码下载 1. 背景 在学习完成HTML/CSS/JS/jQuery零基础入门教程后,自然而然我们要进入Bootstra ...
- 零基础SSM入门教程(0)--教程背景与目录
点此查看 零基础JavaWeb全栈文章目录及源码下载 1. 背景 本教程的出发点是写一个比较条理,比较全面的SSM系列教程,首先造福自己.理顺自己的知识体系,然后也造福大众,因为网上现存的资源相对比较 ...
- Helm3入门教程全系列,26小时轻松掌握Helm
很多人都使用过Ubuntu下的ap-get或者CentOS下的yum, 这两者都是Linux系统下的包管理工具.采用apt-get/yum,应用开发者可以管理应用包之间的依赖关系,发布应用:用户则可以 ...
最新文章
- Cassandra数据模型设计最佳实践
- KernelBuildpackageHowto
- 【应用笔记】【AN001】VC#开发环境下基于以太网的4-20mA电流采集(基于modbus tcp 协议)...
- Kafka的producer案例,Kafka的consumer案例
- c++ 宏 win linux_服务器端开发经验总结 Linux C语言
- C语言 strlcpy函数实现
- 在Word里使用部分正则表达式
- Jexus vs IIS8 非绝对客观对比测试
- 微软直播马上开始,近百岗位等你来,快戳进直播间
- CCF201712试题
- 【Android TV 开发】-->开发汇总
- apache commons-beanutils中BeanUtils和PropertyUtils区别
- 软件工程之软件质量管理
- matlab设置角度和弧度制,matlab 可不可以用角度制不用弧度制?
- java中linechart用法_Line Chart
- 如何让计算机虚拟出多个桌面,多屏显示算什么?win10自带神技能,桌面秒变多屏幕,创建无上限...
- IM学习-认识即时通讯IM(一)
- 计算机导论模板,计算机导论论文提纲范文模板 计算机导论论文大纲怎样写
- 小明发布_发布会不打算开了?网曝小米之家的小米 9 实拍
- C语言之最简单的Hello World!
热门文章
- 2022年第十三届蓝桥杯省赛--难度评价
- office2007每次打开都配置进度_解决Office2007每次启动时出现配置进度的问题
- STM32单片机全自动锂电池容量电量检测放电电流电池电压ACS712
- Windows Terminal美化增强指南
- 研发体系核心代码和文档安全保护方案
- SVN 小乌龟(TortoiseSVN)本地文件更新报错Another process is blocking the working copy database 解决方法
- 程序员写博客如何赚钱「5大盈利方向」
- 数百种 Windows 软件的免费替代品列表
- 放大器设计-光电放大电路噪声分析-理论
- Jmeter基础教程