📅  最后修改于: 2023-12-03 15:39:19.793000             🧑  作者: Mango
给定一个二维矩阵,矩阵中的每个元素都是一个字符。从矩阵的左上角走到右下角,可以沿着四个方向移动。现在要将路径转换为回文路径,即从左上角开始到右下角结束,再从右下角开始到左上角结束,路径上的字符不变。
求将所有路径都转换为回文路径的最小步数。
例如,对于以下矩阵:
a b c
d e f
g h i
左上角到右下角有两条路径:a、d、e、h、i
和a、b、e、h、i
。将这两条路径转换为回文路径的最小步数为1,例如将第一条路径转换为a、d、e、h、e、d、a
即可。
题目要求将所有路径都转换为回文路径,因此需要对从左上角到右下角的所有路径进行处理。对于一条路径,可以分成两段,第一段从左上角到中间点,第二段从右下角到中间点。因为回文路径的特殊性,第一段和第二段在中间点重合的位置上的字符必须相同。
因此,可以从左上角开始遍历矩阵,在遍历的过程中记录从左上角到当前位置的所有路径,并把这些路径以及它们的镜像加入一个列表中。对于每个路径,找到它的中间点,检查中间点上的字符是否和它的镜像路径中的中间点上的字符相同,如果不同,则需要改变中间点上的字符,使得这条路径和它的镜像路径变成回文路径,同时需要记录改变的次数。
具体地,可以使用深度优先搜索(DFS)来遍历所有路径,使用动态规划(DP)的思想来记录路径中的字符和需要改变的次数,从而求出将所有路径转换为回文路径的最小步数。
首先定义一个结构体Point
,表示矩阵中的一个点,包括行号row
和列号col
。
struct Point {
int row;
int col;
};
接下来定义一个结构体Path
,表示从左上角到某个点的路径,包括一个字符数组str
,表示路径上的字符,一个整型变量len
,表示路径的长度,以及一个整型变量count
,表示需要改变的字符数。Path
结构体还需要重载小于(<
)运算符,便于使用std::set
保存路径,用于去重。
struct Path {
char str[100]; // 假设路径长度不超过100
int len;
int count;
bool operator < (const Path& other) const {
int n = std::min(len, other.len);
for (int i = 0; i < n; ++i) {
if (str[i] < other.str[i]) {
return true;
}
else if (str[i] > other.str[i]) {
return false;
}
}
return len < other.len;
}
};
接下来定义一个函数mirror_path
,用于计算一个路径的镜像路径。该函数接受一个Path
类型的路径作为参数,返回另一个Path
类型的路径作为结果。
Path mirror_path(const Path& path) {
Path mirror;
mirror.len = path.len;
mirror.count = path.count;
for (int i = 0; i < path.len; ++i) {
mirror.str[i] = path.str[path.len - i - 1];
}
return mirror;
}
接下来实现深度优先搜索的递归部分。定义一个函数dfs
,接受当前点、目标点、当前路径和已经处理过的路径的std::set
,返回一个Path
类型的结果,表示从当前点到目标点的路径。
对于当前点,如果它已经在处理过的路径中出现过,则说明它已经处理过,直接返回一个空路径表示无法到达目标点。如果当前点就是目标点,则说明已经找到了一条路径,直接返回当前路径。
如果当前点不是目标点,则需要继续向下搜索。首先判断当前点可以向哪些方向(上、下、左、右)走,然后对于每个方向,计算出下一个点的坐标,然后递归调用dfs
函数。如果递归调用的结果不是一个空路径,则说明从当前点到目标点有一条路径,将当前路径和递归调用的结果合并成一条完整的路径,然后返回这条完整路径。
Path dfs(const Point& start, const Point& end, const Path& current, std::set<Path>& processed) {
if (processed.count(current) > 0) {
// 当前路径已经处理过
return Path{ "", 0, INT_MAX };
}
if (start.row == end.row && start.col == end.col) {
// 已经到达目标点
return current;
}
processed.insert(current);
// 在上、下、左、右四个方向中搜索路径
std::vector<Point> directions = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };
Path result{ "", 0, INT_MAX };
for (auto& dir : directions) {
int next_row = start.row + dir.row;
int next_col = start.col + dir.col;
if (next_row < 0 || next_row >= ROWS || next_col < 0 || next_col >= COLS) {
// 下一个点越界,不能搜索
continue;
}
char next_char = matrix[next_row][next_col];
if (next_char == '#') {
// 下一个点是障碍物,不能搜索
continue;
}
Path next_path = current;
next_path.str[current.len] = next_char;
next_path.len++;
auto subresult = dfs({ next_row, next_col }, end, next_path, processed);
if (subresult.len > 0 && subresult.count + current.count < result.count) {
// 找到一条更优的路径
result.len = current.len + subresult.len;
result.count = current.count + subresult.count;
std::copy(current.str, current.str + current.len,
result.str);
std::copy(subresult.str, subresult.str + subresult.len,
result.str + current.len);
}
}
return result;
}
现在可以利用深度优先搜索来找到从左上角到右下角的所有路径,接下来需要计算将这些路径转换为回文路径的最小步数。
首先将所有路径以及它们的镜像路径加入一个std::set
中,这样可以去掉重复的路径。然后遍历std::set
中的每一条路径,找到中间点,并检查中间点上的字符是否和它的镜像路径上的中间点字符相同,如果不同,则需要改变中间点上的字符,使得路径变成回文路径,同时记录所有路径中需要改变的字符数,取其中的最小值即为最小步数。
Path all_paths[MAX_PATHS];
int path_count = 0;
// 从左上角到右下角找到所有路径
Path path = dfs({ 0, 0 }, { ROWS - 1, COLS - 1 },
Path{ "", 0, 0 }, std::set<Path>{});
while (path.len > 0) {
// 将当前路径加入列表中,并计算它的镜像路径
all_paths[path_count] = path;
path_count++;
auto mirror = mirror_path(path);
if (path < mirror) {
all_paths[path_count] = mirror;
path_count++;
}
path = dfs({ 0, 0 }, { ROWS - 1, COLS - 1 },
Path{ "", 0, 0 }, std::set<Path>{ all_paths, all_paths + path_count });
}
// 计算将所有路径转换为回文路径的最小步数
int min_steps = INT_MAX;
for (int i = 0; i < path_count; ++i) {
// 找到当前路径的中心位置
int length = all_paths[i].len;
int center = (length - 1) / 2;
// 检查顺序路径和镜像路径的中间位置的字符是否相等
char ch1 = all_paths[i].str[center];
char ch2 = all_paths[i].str[length - 1 - center];
if (ch1 == ch2) {
continue;
}
int count = std::abs(ch1 - ch2);
// 计算将当前路径和它的镜像路径都改变到回文路径的最小步数
int steps = 0;
for (int j = 0; j < path_count; ++j) {
int length2 = all_paths[j].len;
int center2 = (length2 - 1) / 2;
char ch3 = all_paths[j].str[center2];
char ch4 = all_paths[j].str[length2 - 1 - center2];
if (all_paths[i].len == all_paths[j].len && ch1 == ch3 && ch2 == ch4) {
continue;
}
int count2 = std::abs(ch3 - ch4);
steps += std::min(count, count2);
}
min_steps = std::min(min_steps, steps);
}
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
const int ROWS = 3;
const int COLS = 3;
const int MAX_PATHS = 50000;
char matrix[ROWS][COLS] = {
{ 'a', 'b', 'c' },
{ 'd', 'e', 'f' },
{ 'g', 'h', 'i' },
};
struct Point {
int row;
int col;
};
struct Path {
char str[100]; // 假设路径长度不超过100
int len;
int count;
bool operator < (const Path& other) const {
int n = std::min(len, other.len);
for (int i = 0; i < n; ++i) {
if (str[i] < other.str[i]) {
return true;
}
else if (str[i] > other.str[i]) {
return false;
}
}
return len < other.len;
}
};
Path mirror_path(const Path& path) {
Path mirror;
mirror.len = path.len;
mirror.count = path.count;
for (int i = 0; i < path.len; ++i) {
mirror.str[i] = path.str[path.len - i - 1];
}
return mirror;
}
Path dfs(const Point& start, const Point& end, const Path& current, std::set<Path>& processed) {
if (processed.count(current) > 0) {
// 当前路径已经处理过
return Path{ "", 0, INT_MAX };
}
if (start.row == end.row && start.col == end.col) {
// 已经到达目标点
return current;
}
processed.insert(current);
// 在上、下、左、右四个方向中搜索路径
std::vector<Point> directions = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };
Path result{ "", 0, INT_MAX };
for (auto& dir : directions) {
int next_row = start.row + dir.row;
int next_col = start.col + dir.col;
if (next_row < 0 || next_row >= ROWS || next_col < 0 || next_col >= COLS) {
// 下一个点越界,不能搜索
continue;
}
char next_char = matrix[next_row][next_col];
if (next_char == '#') {
// 下一个点是障碍物,不能搜索
continue;
}
Path next_path = current;
next_path.str[current.len] = next_char;
next_path.len++;
auto subresult = dfs({ next_row, next_col }, end, next_path, processed);
if (subresult.len > 0 && subresult.count + current.count < result.count) {
// 找到一条更优的路径
result.len = current.len + subresult.len;
result.count = current.count + subresult.count;
std::copy(current.str, current.str + current.len,
result.str);
std::copy(subresult.str, subresult.str + subresult.len,
result.str + current.len);
}
}
return result;
}
int main() {
Path all_paths[MAX_PATHS];
int path_count = 0;
// 从左上角到右下角找到所有路径
Path path = dfs({ 0, 0 }, { ROWS - 1, COLS - 1 },
Path{ "", 0, 0 }, std::set<Path>{});
while (path.len > 0) {
// 将当前路径加入列表中,并计算它的镜像路径
all_paths[path_count] = path;
path_count++;
auto mirror = mirror_path(path);
if (path < mirror) {
all_paths[path_count] = mirror;
path_count++;
}
path = dfs({ 0, 0 }, { ROWS - 1, COLS - 1 },
Path{ "", 0, 0 }, std::set<Path>{ all_paths, all_paths + path_count });
}
// 计算将所有路径转换为回文路径的最小步数
int min_steps = INT_MAX;
for (int i = 0; i < path_count; ++i) {
// 找到当前路径的中心位置
int length = all_paths[i].len;
int center = (length - 1) / 2;
// 检查顺序路径和镜像路径的中间位置的字符是否相等
char ch1 = all_paths[i].str[center];
char ch2 = all_paths[i].str[length - 1 - center];
if (ch1 == ch2) {
continue;
}
int count = std::abs(ch1 - ch2);
// 计算将当前路径和它的镜像路径都改变到回文路径的最小步数
int steps = 0;
for (int j = 0; j < path_count; ++j) {
int length2 = all_paths[j].len;
int center2 = (length2 - 1) / 2;
char ch3 = all_paths[j].str[center2];
char ch4 = all_paths[j].str[length2 - 1 - center2];
if (all_paths[i].len == all_paths[j].len && ch1 == ch3 && ch2 == ch4) {
continue;
}
int count2 = std::abs(ch3 - ch4);
steps += std::min(count, count2);
}
min_steps = std::min(min_steps, steps);
}
std::cout << "将矩阵中所有路径从左上角到右下角转换为回文路径的最小步骤为:" << min_steps << std::endl;
return 0;
}
以上为本题解,注意格式。