文章目录
- 问题描述
- 思路
- 代码C++
问题描述
描述
定义一个二维数组 N*M ,如 5 × 5 数组下所示:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。入口点为[0,0],既第一格是可以走的路。
数据范围: 2≤n,m≤10 2≤n,m≤10 , 输入的内容只包含 0≤val≤1 0≤val≤1
输入描述:
输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的1表示墙壁,0表示可以走的路。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
输出描述:
左上角到右下角的最短路径,格式如样例所示。
示例1
输入:
5 5
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出:
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
思路
- 前进策略: 一直靠着左侧的墙壁走
- 假设初始方向为col的方向
- 如果能向左走,直接向左转弯,并向前走一步;
- 否则依次尝试:向前走、向右走。注意:
- 向前走时不需要修改当前的前进方向
- 尝试向右走时,无论是否能够向右走出一步,都直接向右转方向,这样连续转几次,就能转出死角
- 当 当前位置 等于 出口位置时结束
- 通过删除重复不必要的足迹使路径最短——已知了只有一条通路
代码C++
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Pos {
int col;
int row;
bool operator==(const Pos& another) const {
return another.col == col && another.row == row;
}
};
class Agent {
public:
int N; // 总行数
int M; // 总列数
vector<vector<int>> data; // 迷宫矩阵
int direction;
vector<Pos> path;
Pos cur_pos;
Pos final_pos;
public:
void get_input() {
cin >> N >> M;
data = vector<vector<int>>(N, vector<int>(M));
for (int i=0; i<N; ++i) {
for (int j=0; j<M; ++j) {
cin >> data[i][j];
}
}
final_pos.row = N - 1;
final_pos.col = M - 1;
cur_pos.row = 0;
cur_pos.col = 0;
path.push_back(cur_pos);
direction = 1;
}
void print_path() {
for (auto& pos : path) {
printf("(%d,%d)\n", pos.row, pos.col);
}
}
bool valid_pos(const Pos& pos) {
if (pos.col >= 0 && pos.col < M &&
pos.row >= 0 && pos.row < N &&
data[pos.row][pos.col] == 0)
{
return true;
} else {
return false;
}
}
bool move_forward() {
Pos next_pos = cur_pos;
if (direction == 1) {
next_pos.col += 1;
} else if (direction == 2) {
next_pos.row += 1;
} else if (direction == 3) {
next_pos.col -= 1;
} else {
next_pos.row -= 1;
}
// printf("forwd: %d,%d\n", next_pos.row, next_pos.col);
if (valid_pos(next_pos)) {
cur_pos = next_pos;
path.push_back(next_pos);
return true;
} else {
return false;
}
}
bool move_right() {
Pos next_pos = cur_pos;
if (direction == 1) {
next_pos.row += 1;
} else if (direction == 2) {
next_pos.col -= 1;
} else if (direction == 3) {
next_pos.row -= 1;
} else {
next_pos.col += 1;
}
// printf("right: %d,%d\n", next_pos.row, next_pos.col);
direction = (direction + 1) <= 4 ? (direction + 1) : 1; // 更新方向
if (valid_pos(next_pos)) {
cur_pos = next_pos;
path.push_back(next_pos);
return true;
} else {
return false;
}
}
bool move_left() {
Pos next_pos = cur_pos;
if (direction == 1) {
next_pos.row -= 1;
} else if (direction == 2) {
next_pos.col += 1;
} else if (direction == 3) {
next_pos.row += 1;
} else {
next_pos.col -= 1;
}
// printf("left : %d,%d\n", next_pos.row, next_pos.col);
if (valid_pos(next_pos)) {
cur_pos = next_pos;
direction = (direction - 1) >= 1 ? (direction - 1) : 4; // 更新方向
path.push_back(next_pos);
return true;
} else {
return false;
}
}
// 前进策略: 一直靠着左侧的墙壁走
void find_way() {
while (true) {
if (cur_pos == final_pos) {
break;
}
// 根据 当前 direction 判断前后左右
if (move_left()) { /*左侧是墙壁*/
trim_path();
// printf("向左走\n");
} else {
if (move_forward()) { /*前面能走*/
trim_path();
// printf("向前走\n");
} else {
/*向右能走,注意无论是否能向右走,都直接向右转向了,所以一定能转出去*/
if (move_right()) {
trim_path();
// printf("向右走\n");
}
}
}
}
}
// 移除不必要的足迹: A-B-D-E-B-C-X-Y-Z, B-D-E就是不必要的足迹, 不能有重复的坐标点
void trim_path() {
Pos end = path.back();
for (auto iter = path.begin(); iter != path.end()-1; iter++) {
if (*iter == end) {
for (auto iter2 = iter; iter2 != path.end()-1; iter2) {
path.erase(iter2);
}
break;
}
}
}
};
int main()
{
Agent agent;
agent.get_input();
agent.find_way();
agent.print_path();
return 0;
}