罗密欧与朱丽叶的迷宫问题
问题描述
罗密欧与朱丽叶的迷宫。罗密欧与朱丽叶身处一个m×n的迷宫中,如图所示。每一个方格表示迷宫中的一个房间。这m×n个房间中有一些房间是封闭的,不允许任何人进入。在迷宫中任何位置均可沿8 个方向进入未封闭的房间。罗密欧位于迷宫的(p,q)方格中,他必须找出一条通向朱丽叶所在的(r,s)方格的路。在抵达朱丽叶之前,他必须走遍所有未封闭的房间各一次,而且要使到达朱丽叶的转弯次数为最少。每改变一次前进方向算作转弯一次。请设计一个算法帮助罗密欧找出这样一条道路。
罗密欧与朱丽叶的迷宫
.编程任务:
对于给定的罗密欧与朱丽叶的迷宫,编程计算罗密欧通向朱丽叶的所有最少转弯道路。
.数据输入:
由文件input.txt给出输入数据。第一行有3个正整数n,m,k,分别表示迷宫的行数,列数和封闭的房间数。接下来的k行中,每行2 个正整数,表示被封闭的房间所在的行号和列号。最后的2 行,每行也有2 个正整数,分别表示罗密欧所处的方格(p,q)和朱丽叶所处的方格(r,s)。
.结果输出:
将计算出的罗密欧通向朱丽叶的最少转弯次数和有多少条不同的最少转弯道路输出到文件output.txt。文件的第一行是最少转弯次数。文件的第2 行是不同的最少转弯道路数。接下来的n行每行m个数,表示迷宫的一条最少转弯道路。A[i][j]=k表示第k步到达方格(i,j);A[i][j]=-1 表示方格(i,j)是封闭的。
如果罗密欧无法通向朱丽叶则输出“No Solution!”。
输入文件示例
input.txt
3 4 2
1 2
3 4
1 1
2 2
输出文件示例
output.txt
6
7
1 -1 9 8
2 10 6 7
3 4 5 -1
#include <iostream>
#include <fstream>
using namespace std;
//罗密欧与朱丽叶的迷宫问题
/*
该题可以使用回溯的方式来解决,因为仔细观察之后,你其实可以发现,罗密欧无论在每一个位置最多有
8中选择,其实就是一个节点到达下一层的路径;
回溯法最重要的就是判断这是一种甚么类型的问题? 无非两种:1. 子集问题 2. 全排列问题
问题需要把所有的可通过房间都走过一遍且在每个房间都有八种选择--->无非就是子集问题
起始点时根节点,叶结点时终点
之后就是常规的回溯模板套进去!
*/
struct Point
{
int x, y;
};
Point luo;
Point ye;
Point pos;
// 定义八个方向
int dx[8] = { 1, 0, -1, 0, 1, 1, -1, -1 }; //八个方向
int dy[8] = { 0, 1, 0, -1, 1, -1, 1, -1 };
const int MAX = 10;
int n, m, k;
int board[MAX][MAX];
int best[MAX][MAX];
int curr_rotation = 0; //转弯次数
int min_rotation = 100000; //最少转弯次数
int min_count = 0; //不同的最少转弯道路数
bool flag = false;
bool Point_check(Point pos) {
if (pos.x > 0 && pos.x <= n && pos.y > 0 && pos.y <= m && board[pos.x][pos.y] == 0)
return true;
return false;
}
// 更新当前最少转弯情况下的路线
void upgrade() {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
best[i][j] = board[i][j];
flag = true;
}
// 回溯算法--->形式参数表示的是traceBack的层数
void traceBack(int depth, Point pos, int di) {
/*
回溯法的终止条件,
当 把所有的空房间都遍历一般
且 当前到达的位置是朱丽叶的位置
且 当前转弯的次数少于等于历史的次数
*/
if (depth == m * n - k && pos.x == ye.x && pos.y == ye.y && curr_rotation <= min_rotation) {
/*
如果当前的curr_count小于min_rotation更新min_rotation、min_count以及路径图
*/
if (curr_rotation < min_rotation) {
min_rotation = curr_rotation;
min_count = 1;
// 更新路径图
upgrade();
}
else {
min_count++;
}
return;
}
else {
// 剪枝策略-----当到达这个位置的时候curr_rotation已经大于min_rotation那么进行剪枝
for (int i = 0; i < 8; i++) {
// 通过走的方向,计算下一个位置
Point next_pos;
next_pos.x = pos.x + dx[i];
next_pos.y = pos.y + dy[i];
// 每次走一步需要判断你下一个地点的位置是否合法
if (Point_check(next_pos)) {
board[next_pos.x][next_pos.y] = depth + 1;
if (depth > 1 && di != i)
curr_rotation++;
if (curr_rotation <= min_rotation)
traceBack(depth + 1, next_pos, i);
// 进行回溯
board[next_pos.x][next_pos.y] = 0;
if (depth > 1 && di != i)
curr_rotation--;
}
}
}
}
int main() {
// 迷宫的初始化
memset(board, 0, sizeof(board));
memset(best,0,sizeof(board));
// 文件的输入
ifstream datain("input_data_1.txt");
cout << "输入迷宫的宽度:"; datain >> n; cout << n<<endl;
cout << "\n输入迷宫的长度:"; datain >> m; cout << m << endl;
cout << "\n输入封闭房间个数:"; datain >> k; cout << k << endl;
// 封闭房间数据的输入
for (int i = 0; i < k; i++) {
Point forbidden_rooms;
datain >> forbidden_rooms.x >> forbidden_rooms.y;
board[forbidden_rooms.x][forbidden_rooms.y] = -1;
}
// 输入罗密欧和朱丽叶的位置信息
datain >> luo.x >> luo.y;
cout << endl << "罗密欧位置坐标:[" << luo.x << ", " << luo.y << "]" << endl;
board[luo.x][luo.y] = 1;
datain >> ye.x >> ye.y;
cout << endl << "朱丽叶位置坐标:[" << ye.x << ", " << ye.y << "]" << endl;
// 回溯算法开始
traceBack(1, luo, 0);
// 把结果输出到txt文件中
ofstream dataout("output_data1.txt", ios::trunc);
if (flag) {
dataout << min_rotation << endl;
cout << "\n最小转弯次数:" << min_rotation << endl;
dataout << min_count << endl;
cout << "\n最小转弯次数的转弯道路数:" << min_count << endl;
cout << "行走的路线图:" << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dataout << best[i][j] << " ";
cout << best[i][j] << " ";
}
dataout << endl;
cout << endl;
}
}
else {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
//dataout << best[i][j] << " ";
cout << board[i][j] << " ";
}
//dataout << endl;
cout << endl;
}
dataout << "No Solution!" << endl;
cout<< "\nNo Solution!" << endl;
}
return 0;
}