首页 > 编程语言 >【计算机算法设计与分析】罗密欧与朱丽叶的迷宫问题(C++_回溯法)

【计算机算法设计与分析】罗密欧与朱丽叶的迷宫问题(C++_回溯法)

时间:2024-01-19 14:32:38浏览次数:22  
标签:Map int 转弯 迷宫 罗密欧 C++ 回溯 朱丽叶



文章目录

  • 题目描述
  • 测试样例
  • 算法原理
  • 算法实现
  • 参考资料


题目描述

罗密欧与朱丽叶的迷宫。罗密欧与朱丽叶身处一个m x n 的迷宫中,如图所示。每一个方恪表示迷宫中的一个房间。这m x n个房间中有一些房间是封闭的。不允任何人进入。在迷宫中任何位置均可沿8个方向进入未封闭的房间。罗密欧位于迷宫的(p,q)方格中,他必须找出一条通向朱丽叶所在的(r,s)格的路,在朱丽叶方格之前,他必须走遍所有未封闭的房间各一次,而且要使到达朱丽叶方格的转弯次数为最少。 每改变一次前进方向算作转弯一次。请设计一个算法帮助罗密欧找出这样一条道路。

【计算机算法设计与分析】罗密欧与朱丽叶的迷宫问题(C++_回溯法)_算法

测试样例

输入:
第一行有3个正整数n,m,k,分别表示迷宫的行数,列数和封闭的房间数。接下来的k行中,每行2个正整数,表示被封闭的房间所在的行号和列号。最后的2行,每行也有2个正整数,分别表示罗密欧所处的方格(p,q)和朱丽叶所处的方格(r,s)。

3 4 2
1 2
3 4
1 1
2 2

输出:
将计算出的罗密欧通向朱丽叶的最少转弯次数和有多少条不同的最少转弯道路。文件的第一行是最少转弯次数。文件的第2行是不同的最少转弯道路数。接下来的n行每行m个数,表示迷宫的一条最少转弯道路。A[i][j]=k表示第k步到达方格(i,j);A[i][j]=-1 表示方格(i,j)是封闭的。

6
7
1 -1 9 8
2 10 6 7
3 4 5 -1

算法原理

这道题相比于以往的回溯算法模式相同,但编写难度更高。对于这道题的分析还是从终止条件和递归体两部分着手:

  • 终止条件:当遍历的房间数等于所有未封闭房间数且到达目的地(x == r && y == s && roads == (m * n- k))时本轮递归就结束了,再判别本轮递归是否转弯次数更少,更新答案。
  • 递归体:对于每次选择下一个点我们都有八个方位可以去(上,下,左,右, 左上,右上,左下,右下),那么每轮递归我们只需要依次遍历八个方位即可,不能重复遍历也不能走入非法空间(超出空间范围或进入封闭房间)。由于是回溯算法且有很多变量是全局变量,我们必须在递归后恢复所有数值,不能影响下一步遍历。
  • 剪枝条件(可选):通过中间结果判别直接裁剪掉这部分解空间,可以加快运算效率。由于我们要求最少的转弯次数,所以当中间某个走法的转弯次数已经比之前记录过的转弯次数更多的时候就没有必要再继续运算了,进行剪枝。

对于任何一个回溯法题解,你必须想到其递归终止条件,以及在递归体中用什么样的顺序遍历所有空间。通常还需要考虑是否要设置一个visited数组以保证不重复遍历,本题由于图所蕴含的信息非常少,就直接使用承载输入信息(封闭空间位置)的图(Map)用来记录遍历信息。

算法实现

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

static int Map[100][100], m, n, k, p, q, r, s, minz = INF, roadnums = 0, minMap[100][100];
static int diry[8] = { 0, 0, -1, 1, -1, -1, 1, 1 }, dirx[8] = { -1, 1, 0, 0, -1, 1, -1, 1 };
//方向(dirx[i], diry[i])
static int judge(int x, int y) {  //判断点是否有效
	if (x > 0 && x <= m && y > 0 && y <= n && Map[x][y] != -1)
		return 1;
	else
		return 0;
}
//方向标志dir:0, 1, 2, 3, 4,5,6,7,8->(初始,上,下,左,右, 左上,右上,左下,右下)
static void dfs(int x, int y, int znums, int roads, int dir, int pace) {//(x, y)当前方位,znums转弯次数,roads访问房间数,pace当前是第几步
	if (x == r && y == s) {  //见到朱丽叶
		if (roads == (m * n- k)) {  //走完了所有非封闭房间且转弯次数更少
			if (znums < minz) {
				roadnums = 1;//最少转弯道路数更新
				minz = min(minz, znums);
				for (int i = 1; i <= m; i++)
					for (int j = 1; j <= n; j++)
						minMap[i][j] = Map[i][j];
			}
			else if (znums == minz)//不同的最少转弯道路数
				roadnums++;
		}
		return;
	}
	for (int i = 0; i < 8; i++) {
		int nx = x + dirx[i], ny = y + diry[i];
		int j = judge(nx, ny);
		if (Map[nx][ny] || !j)//不走回头路且只走非封闭房间
			continue;
		Map[nx][ny] = ++pace;
		int curdir = i+1;//判断是否拐弯
		if (dir && dir != curdir)
			znums++;
		if (znums > minz)//剪枝
			return;
		int tempdir = dir;//暂存数值,为了回溯时复原
		dir = curdir;
		dfs(nx, ny, znums, roads+1, dir, pace);
		Map[nx][ny] = 0;
		if(dir!= tempdir)
			znums--;
		dir = tempdir;
		pace--;
	}
}

void main()
{
	memset(Map, 0, sizeof(Map));
	cin >> m >> n >> k;
	for (int i = 0; i < k; i++) {
		int temp1, temp2;
		cin >> temp1 >> temp2;
		Map[temp1][temp2] = -1;  //封闭房间
	}
	cin >> p >> q >> r >> s;	
	Map[p][q] = 1;
	dfs(p, q, 0, 1, 0, 1);
	cout << minz << "\n" << roadnums << endl;;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) 
			cout << minMap[i][j] << "\t";
		cout << endl;
	}
}
/*
3 4 2
1 2
3 4
1 1
2 2
*/

参考资料

  1. 罗密欧与朱丽叶的迷宫问题----回溯法
  2. 罗密欧与朱丽叶的迷宫问题_dfs


标签:Map,int,转弯,迷宫,罗密欧,C++,回溯,朱丽叶
From: https://blog.51cto.com/u_16165815/9329636

相关文章

  • C++ Qt开发:Charts与数据库组件联动
    Qt是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍Charts组件与QSql数据库组件的常用方法及灵活运用。在之前的文章中详细介绍了关于QCharts绘图组件......
  • C/C++ 实现动态资源文件释放
    当我们开发Windows应用程序时,通常会涉及到使用资源(Resource)的情况。资源可以包括图标、位图、字符串等,它们以二进制形式嵌入到可执行文件中。在某些情况下,我们可能需要从可执行文件中提取自定义资源并保存为独立的文件。在这篇博客文章中,我们将讨论如何使用C++和WinAPI实现这个目标......
  • C++ 邮件槽ShellCode跨进程传输
    在计算机安全领域,进程间通信(IPC)一直是一个备受关注的话题。在本文中,我们将探讨如何使用Windows邮件槽(Mailslot)实现ShellCode的跨进程传输。邮件槽提供了一种简单而有效的单向通信机制,使得任何进程都能够成为邮件槽服务器,并通过UDP通信向其他进程发送数据。邮件槽是Windows操作系统......
  • C++ 共享内存ShellCode跨进程传输
    在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode,从而实现一种巧妙的本地传输手段。如果你问我为何在本地了......
  • C++简史
    喜欢本篇文章速速点赞评论⭐收藏MISRAC++:2023,MISRA®C++标准的下一个版本,来了!为了帮助您做好准备,我们介绍了Perforce首席技术支持工程师FrankvandenBeuken博士撰写的MISRAC++:2023博客系列的第二部分。在这篇博客中,我们将深入探讨C++的历史、编程语言多年来的发展历......
  • KY129 简单计算器C++
    、这是目前阶段做的最难最吃力的题目。调试了一遍又一遍去看逻辑上出现的各种问题。。。#include<iostream>#include<string>#include<stack>#include<map>usingnamespacestd;intmain(){map<char,int>m={{'+',0},{'-',0},......
  • C++基础
    一、理论      1、虚函数 1.1、定义:​虚函数就是在类中被关键字virtual声明的函数,一般只在基类中声明虚函数。    1.2、规则:                   1、虚函数必须是......
  • C++多线程
    C++多线程的语法以及使用1.线程的创建首先创建一个多线程入口函数threadmain,threadmain函数体中完成子线程所要做的事。接着在主函数中创建线程对象th,调用构造函数,并传递一个函数指针作为入口函数:threadth(treadmain);入口函数为thread构造函数的参数。之后在主线程中......
  • 20. 有效的括号C++
    括号匹配用栈是解决是最简单那的。遇到左括号就入栈。遇到右括号就出栈,然后看是否匹配。这里再用一个map把括号数字化会更简单。classSolution{public:boolisValid(strings){map<char,int>m={{'(',1},{')',-1},{'{',2},{'}',-......
  • 回溯过程中降重剪枝
    这题跟之前组合问题不同之处在于给的数组里面的元素是有重复的。如果按照之前方法处理的话,就会得到重复的集合。看了卡哥的方法,知道这个去重是是树层去重,横向的;不是树枝去重,纵向的。这除了和前一个元素比较,还要加一个visit数组。如果前一个元素的visit是false就符合条件。这......