假设有一个矩阵,四个边都是堵住的,矩阵内部有若干障碍物,现在有两个点,用若干条直线连接两个点,且直接不能穿越障碍物,那么至少需要几条直线呢?

前面写过一个迷宫中求路径的程序( http://blog.csdn.net/royt/article/details/74848796 ),用到了递归的方式求解。先用类似的方法定义一个满足要求的矩阵。对于迷宫的矩阵,四个边都布满了障碍物且分别有一个入口和出口位于其上,在这个问题里面我们可以简化一下去掉四边的围墙,反正寻找路径时坐标点满足 0<=x<HEIGHT 和 0<=y<WIDTH 即可。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>// height & width must be positive ingeters and cannot both be 1
#define HEIGHT 24
#define WIDTH  36// 2-dimensional array that represents the maze
int matrix[HEIGHT][WIDTH];
int min_nLines = INT_MAX;// 4 directions: {right, down, left, up}
const int DIRECTIONS[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};// status of a cell in the matrix
enum status { BLOCKED = -1, START = 0, UNBLOCKED = INT_MAX - 1, END = INT_MAX };

同样定义了寻找路径的四个方向,矩阵上每个点的状态要么为障碍(-1),要么为空白格(极大值),在计算之后其值更新为到达起点的步数(也就是连通所需的最小直线数量)。那么对于起始点,这个值当然为0,对于其他空白和终点,可以先设置一个非常大的正整数,为了表示区别将终点设置为INT_MAX,其他空白为INT_MAX - 1。接下来则是生成矩阵的程序,没啥特别之处,先是放置障碍物的函数,其参数定义了空白和障碍物的比例,这个值太小则可能彻底堵死找不到通路,太大则显得矩阵空空荡荡,连通的路径不够百曲千回,演示效果不佳,一般取3或4就差不多了。接着是随机选取起点和终点,两点不允许重合,为了简单起见,即使不小心选到障碍物也不用计较,就当它原本是空白格。最后是打印矩阵,可以人为的给四周加上一圈围墙,每一处空白会显示为距离起点的步数(当然有些空白是不可达的,假如正好被障碍物围起来)。这一切看来都没什么问题。

void placeObstacle(int ratio)
{// ratio = Unblocked / Blockedint num_blocked = 0, max_num_blocked = HEIGHT * WIDTH / (1 + ratio);for (int i = 0; i < HEIGHT; ++i){for (int j = 0; j < WIDTH; ++j){matrix[i][j] = (num_blocked < max_num_blocked && rand() % (1 + ratio) == 0) ? BLOCKED : UNBLOCKED;if (matrix[i][j] == BLOCKED) ++num_blocked;}}
}void setupEndpoint(int *a, int *b, int *c, int *d)
{// find 2 different points (*a, *b) and (*c, *d) in a rectangle (HEIGHT * WIDTH)*a = rand() % HEIGHT;*b = rand() % WIDTH;do {*c = rand() % HEIGHT;*d = rand() % WIDTH;}while (*a == *c && *b == *d);matrix[*a][*b] = START;matrix[*c][*d] = END;
}void printMatrix(void)
{for (int col = 0; col < WIDTH + 2; ++col){printf("█");}printf("\n");for (int row = 0; row < HEIGHT; ++row){printf("█");for (int col = 0; col < WIDTH; ++col){if (matrix[row][col] == BLOCKED)printf("■");else if (matrix[row][col] == UNBLOCKED)printf("  ");else if (matrix[row][col] == START)printf("☆");else if (matrix[row][col] == END)printf("◎");elseprintf("%2d", matrix[row][col]);}printf("█\n");}for (int col = 0; col < WIDTH + 2; ++col){printf("█");}printf("\n");
}

接下来是主函数,其中最关键的就是调用SearchPath这个寻找路径的函数。

int main(void)
{int start_row, start_col;int end_row, end_col;srand((unsigned)time(0));placeObstacle(3);setupEndpoint(&start_row, &start_col, &end_row, &end_col);printMatrix();printf("\n");# searchPath(...);matrix[end_row][end_col] = END;printMatrix();if (min_nLines < INT_MAX)printf("At least %d lines needed to connect 2 points\n", min_nLines);elseprintf("Cannot reach destination\n");return 0;
}

受到前面那个迷宫问题的影响,我没有进行任何思索就写了一个类似的SearchPath递归函数,形式和之前那个差不多,由于打算把从起点到任意空白处的步数都计算出来,所以没有设置终止条件。从起点开始,依次选定各个方向,将步数加1后一步步往前探索空白格直到遇见障碍物,若之前计算过的空白格的步数且大于当前数值,那么保留当前值并在该点进行递归调用,如下所示:

void searchPath(int start_row, int start_col, int end_row, int end_col, int num_of_lines)
{++num_of_lines;for (int d = 0; d < 4; ++d){for (int next_row = start_row + DIRECTIONS[d][0], next_col = start_col + DIRECTIONS[d][1];next_row >= 0 && next_row < HEIGHT && next_col >=0 && next_col < WIDTH \&& matrix[next_row][next_col] != BLOCKED;next_row += DIRECTIONS[d][0], next_col += DIRECTIONS[d][1]){if (matrix[next_row][next_col] > num_of_lines){matrix[next_row][next_col] = num_of_lines;searchPath(next_row, next_col, end_row, end_col, num_of_lines);}if (next_row == end_row && next_col == end_col && matrix[next_row][next_col] < min_nLines){min_nLines = matrix[next_row][next_col];}}}
}

事实上这个方法相当糟糕,计算效率低下。试想一下假如障碍物非常稀少甚至矩阵就是空旷的,连接任意两点最多只需要两条线段,然而我们一步一步前进,不停递归,绕来绕去的不断撞上以前计算过的空白格并更新之。举个最简单的例子,本来沿着一个点朝一条直线看过去,上面的所有空格都该具有同样的步数N,而采用递归的方式,每走一步进入一次递归,那么沿途步数会变成N+1, N+2, N+3, N+4...最后从递归里面退出继续走内层的循环,这些坐标的步数又被还原成N,那么为何不一开始就将四个方向上能直达的点的步数设置为N呢?再仔细想想,这不就是一个广度优先的搜索吗,而前面的递归方法是深度优先的,放到这个题目中显然不适用。于是重新实现了这个SearchPath函数,广度搜索通常需要一个辅助队列,为了方便这里不用链表而是直接开一个大的二维数组,每行可以存储单个空白格的两个坐标值。从起点开始扩散,一旦位于四条射线上的空白格的步数比原来的小,那么该坐标被加入队列准备进行下一次尝试,代码如下:

void searchPath(int start_row, int start_col, int end_row, int end_col)
{int arr[HEIGHT * WIDTH][2] = {{start_row, start_col}};int head = 0, tail = 1;while (head != tail){int start_row = arr[head][0], start_col = arr[head][1];int num_of_lines = matrix[start_row][start_col] + 1;for (int d = 0; d < 4; ++d){for (int next_row = start_row + DIRECTIONS[d][0], next_col = start_col + DIRECTIONS[d][1];next_row >= 0 && next_row < HEIGHT && next_col >=0 && next_col < WIDTH \&& matrix[next_row][next_col] != BLOCKED;next_row += DIRECTIONS[d][0], next_col += DIRECTIONS[d][1]){if (matrix[next_row][next_col] > num_of_lines){matrix[next_row][next_col] = num_of_lines;arr[tail][0] = next_row;arr[tail][1] = next_col;tail = (tail + 1) % (HEIGHT * WIDTH);}if (next_row == end_row && next_col == end_col && matrix[next_row][next_col] < min_nLines){min_nLines = matrix[next_row][next_col];}}}head = (head + 1) % (HEIGHT * WIDTH);}
}

采用这个新函数再试了下,程序运行速度比之前不知快了多少倍。由此要吸取教训,遇到问题时先思考,而不是冒然套用以往的经验,经验固然能帮助解决一些问题,但是慎重的思考还是要放在第一位。在这个程序的运行如下所示:

平时爱玩的连连看,满足消除的条件无非就是两个方块间的连线数最多为3,每消去一对方块就要做一次检测看看是否还能继续游戏。哪怕初始生成的牌面是有办法完全消除的,但玩家执行的步骤还是可能导致陷入死局,所以游戏里面常有“全部重排”的道具。即时检测应该不会太耗时间,毕竟只要有一对牌可消去便可终止测试,况且只需要完成步数为3的广度搜索就行了。即便是牌的数量非常密集需要搜个遍才能终止,这个过程以计算机的视角来看也不会太耗时(在这种情况下单张牌的搜索其实耗时少,四周都密不透风嘛)。实际开发中或许有更快的办法。

布满障碍物的矩阵中连接两点所需的最小直线数目相关推荐

  1. 如何将cell元胞中的数据转化为矩阵中的数据

    将cell中的数据转化成为矩阵中的数据只需用cell2mat函数即可 运行后得到的结果如下:

  2. 地铁线路图中任意两点间所有路径高效算法

    在求图线任意两点间最短路径时,利用floyd.dijdstra等成熟的算法可以求得,效率还不错.但要求换乘最少.最舒适等路径时,需要求线网图中任意两个点的所有路径,然后根据条件筛选,以上算法无能为力. ...

  3. LeetCode-剑指 Offer 12. 矩阵中的路径

    剑指 Offer 12. 矩阵中的路径 思路一:DFS+回溯 DFS 解析: 递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j ,当前目标字符在 word 中的索引 k . 终止条件: ...

  4. 【Matlab】找到矩阵中每个连通域的最小值

    1.Introduction 连通域在我浅薄的图像处理知识中,一直是很神圣的存在.最近想在Matlab里实现的时候,发现竟然一个函数就解决了,Matlab真香! 2.Materials and met ...

  5. numpy找到矩阵中不同元素的种类_基于NumPy和图像分类的人工神经网络构建

    基于NumPy和图像分类的人工神经网络构建 本文利用NumPy系统在Python中构建人工神经网络,以便为Fruits360数据集执行图像分类应用程序. 本文提及的所有内容(即图像和源代码,不包括Fr ...

  6. 矩阵中的最长递增路径

    矩阵中的最长递增路径 文章目录 矩阵中的最长递增路径 一.问题描述 **方法一:朴素的深度优先搜索 [超时]** **方法二:记忆化深度优先搜索 [通过]** **方法三:动态规划** **方法四:广 ...

  7. 使用游标正确提取SQL Server数据并将其放置在Reporting Services矩阵中

    介绍 ( Introduction ) In our last two chats, we discussed enterprises that have had financial years th ...

  8. matlab 连通域数量,【Matlab】找到矩阵中每个连通域的最小值

    [Matlab]找到矩阵中每个连通域的最小值 [Matlab]找到矩阵中每个连通域的最小值 连通域在我浅薄的图像处理知识中,一直是很神圣的存在.最近想在Matlab里实现的时候,发现竟然一个函数就解决 ...

  9. 猿创征文|pandas实现将矩阵导出到excel+对矩阵进行连接

    代码链接:https://download.csdn.net/download/weixin_42295969/86506630 文章目录 pandas实现将矩阵导出到excel 简单的实现 进行内容 ...

最新文章

  1. FF称签约美国百年顶级投行Stifel;Facebook隐私泄露继续发酵,黑客明码标卖聊天信息 | 雷锋早报...
  2. 文件包含和文件上传结合
  3. 【LiveVideoStack线上分享】— 视频生产环境下的音视频解决方案
  4. [ACM] hdu 1754 I Hate It (线段树,单点更新)
  5. 《python网络数据采集》读后感 第六章:读取文档
  6. 【mysql技术内幕1】mysql基础架构-一条SQL查询语句是如何执行的
  7. mybatis 显示 sql日志
  8. Spark 广播变量和累加器
  9. JAVA和JAVAC 命令行
  10. MySQL中删除表中并不存在的数据不报错
  11. php在html中if,html里的if注释怎么使用
  12. 参加了feedsky的博客大赛
  13. GMS:基于网格运动统计的快速极度鲁棒的特征匹配
  14. 防红域名生成的3种方法介绍
  15. X-Space二级(三级)域名配置
  16. 大数据高级开发工程师——Spark学习笔记(6)
  17. 《中文文本信息抽取模型与方法研究》5:基于论元结构的事件要素及其角色识别
  18. 【数据结构】---时间复杂度与空间复杂度
  19. react路由:路由传参params、search、state
  20. centos7开机自动连接网络和固定IP配置

热门文章

  1. 比荒野行动还惨的吃鸡手游:都被外挂给毁了,现在全是人机
  2. node.js----microsoft jscript 运行时错误 800a138f
  3. CrossOver2023mac切换win双系统虚拟机
  4. 小白立创机械狗从零到成品总结
  5. MIT机械狗方案电机连接测试程序
  6. Android开启闪光灯的几种办法
  7. 芝村乡穷人怎样理财更靠谱呢?
  8. apache poi word表格样式(表格固定间隔,表格字体颜色)、段落样式(段落字体颜色,格式)
  9. render process gone
  10. UVA - 524:Prime Ring Problem