📌  相关文章
📜  将矩阵中所有路径从左上角到右下角转换为回文路径的最小步骤(1)

📅  最后修改于: 2023-12-03 15:39:19.793000             🧑  作者: Mango

将矩阵中所有路径从左上角到右下角转换为回文路径的最小步骤

问题描述

给定一个二维矩阵,矩阵中的每个元素都是一个字符。从矩阵的左上角走到右下角,可以沿着四个方向移动。现在要将路径转换为回文路径,即从左上角开始到右下角结束,再从右下角开始到左上角结束,路径上的字符不变。

求将所有路径都转换为回文路径的最小步数。

例如,对于以下矩阵:

a b c
d e f
g h i

左上角到右下角有两条路径:a、d、e、h、ia、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;
}

以上为本题解,注意格式。