【回溯法】矩阵中的路径,机器人的运动范围

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82381848

回溯法

回溯法是做了剪枝改进的穷举法,适合由多个步骤组成,并且每个步骤都有多个选项组成的问题。回溯法的解空间树到达叶结点时,如果在叶子结点的状态满足题目的约束条件,那么就找到了一个可行的解决方案。

回溯二字在于,如果在解空间树上的某个结点不满足约束条件,那么就退回到上一结点,再尝试其它选项;如果全部试过都不行,再向上回溯。

面试题12:矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用下划线标出)。但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

路径可以被看成一个栈,当第n个字符周围都找不到第n+1个字符时,向上回溯一步。由于题目要求路径不能进入重复的矩阵格子,所以要定义一个布尔矩阵来标识路径是否已经进入了每个格子。

#include<bits/stdc++.h>
using namespace std;

// 参数:
//        matrix:      字符矩阵首地址
//        rows:        矩阵的行数
//        cols:        矩阵的列数
//        row:         当前行
//        col:         当前列
//        str:         要检验的字符序列数组
//        pathLength:  当下这个点匹配的路径长度
//        visited:     访问情况矩阵
// 返回值:
//        继续向后匹配,true表示成功匹配,false表示匹配失败
bool hasPathCore(const char* matrix, int rows, int cols, int row,
                 int col, const char* str, int& pathLength, bool* visited) {
    if(str[pathLength] == '\0')//如果已经匹配完整个字符序列,到结尾了
        return true;//自然直接返回true,表示已完成整个字符串匹配

    bool hasPath = false;//记录是否存在该位置的匹配
    //如果游标在合法范围内,且这个位置的值正好匹配,且没有访问过
    if(row >= 0 && row < rows && col >= 0 && col < cols
            && matrix[row * cols + col] == str[pathLength]
            && !visited[row * cols + col]) {
        ++pathLength;//路径向前+1
        visited[row * cols + col] = true;//将这个位置标记为访问过了

        //递归调用自身,向左/上/右/下四个方向匹配,只要有一个匹配成功就短路掉
        hasPath = hasPathCore(matrix, rows, cols, row, col - 1,
                              str, pathLength, visited)
                  || hasPathCore(matrix, rows, cols, row - 1, col,
                                 str, pathLength, visited)
                  || hasPathCore(matrix, rows, cols, row, col + 1,
                                 str, pathLength, visited)
                  || hasPathCore(matrix, rows, cols, row + 1, col,
                                 str, pathLength, visited);
        //如果四个方向都不存在匹配,说明这个位置走错了
        if(!hasPath) {
            --pathLength;//向后倒退一步
            visited[row * cols + col] = false;//这个位置取消标记
        }
    }
    return hasPath;//返回从这个位置能否完成匹配
}

// 参数:
//        matrix:      字符矩阵首地址
//        rows:        矩阵的行数
//        cols:        矩阵的列数
//        str:         要检验的字符序列数组
// 返回值:
//        true表示找到,false表示找不到
bool hasPath(const char* matrix, int rows, int cols, const char* str) {
    //输入合法性检查
    if(matrix == nullptr || rows < 1 || cols < 1 || str == nullptr)
        return false;

    //和字符矩阵大小一样的布尔值矩阵,用来标识某个位置是否访问过了
    bool *visited = new bool[rows * cols];
    memset(visited, 0, rows * cols);//设置为0即设置为false

    int pathLength = 0;//路径的长度,初始设为0
    //遍历整个矩阵
    for(int row = 0; row < rows; ++row) {
        for(int col = 0; col < cols; ++col) {
            //调用匹配的函数,从矩阵上的每个点开始尝试能否完成匹配
            if(hasPathCore(matrix, rows, cols, row, col, str,
                           pathLength, visited)) {
                delete[] visited;//书上的代码缺这一行,成功的情况也要正确回收 
                return true;
                //只要完成一次匹配就返回true了,所以不用再清除标记矩阵
                //而是将其彻底回收 
            }
        }
    }
    delete[] visited;//释放辅助矩阵的空间
    return false;//运行至此,说明匹配失败,返回false 
}


//ABTG
//CFCS
//JDEH
int main() {
    const char* matrix = "ABTGCFCSJDEH";
    const char* str = "BFCE";
    cout<<boolalpha<<hasPath(matrix,4,4,str)<<endl;
    return 0;
}

面试题13:机器人的运动范围

地上有一个m行n列的方格。一个机器人从坐标(0, 0)的格子开始移动,它每一次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35, 37),因为3+5+3+7=18。但它不能进入方格(35, 38),因为3+5+3+8=19。请问该机器人能够到达多少个格子?

也是类似的问题,除了限制条件不同之外,这个问题是要求能够到达多少格子,所以四个方向的情况要加起来,而不是像上一题那样用短路或。

#include<bits/stdc++.h>
using namespace std;

/*声明部分*/
int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited);
bool check(int threshold, int rows, int cols, int row, int col, bool* visited);
int getDigitSum(int number);

// 参数:
//        threshold:      阈值,即题目中的k值
//        rows:           矩阵的行数
//        cols:           矩阵的列数
// 返回值:
//        题目要求的结果,即机器人可达的格子数目
int movingCount(int threshold, int rows, int cols) {
    //输入合法性检查
    if(threshold < 0 || rows <= 0 || cols <= 0)
        return 0;
    //创建访问情况矩阵,全部初始化为false
    bool *visited = new bool[rows * cols];
    for(int i = 0; i < rows * cols; ++i)
        visited[i] = false;
    //从左上角(0,0)点开始,调用这个搜寻可达点的函数计算出可达点数目
    int count = movingCountCore(threshold, rows, cols,
                                0, 0, visited);
    delete[] visited;//释放辅助空间(访问情况矩阵)
    return count;
}

// 参数:
//        threshold:      阈值,即题目中的k值
//        rows:           矩阵的行数
//        cols:           矩阵的列数
//        row:            当前行
//        col:            当前列
//        visited:        访问情况矩阵
// 返回值:
//        从该点开始(有访问情况矩阵限制不重复),新发现的可达点数目
int movingCountCore(int threshold, int rows, int cols, int row,
                    int col, bool* visited) {
    int count = 0;//计数(新发现的可达点数目),从0开始
    //检查当前点是可以进入的
    if(check(threshold, rows, cols, row, col, visited)) {
        visited[row * cols + col] = true;//将该点标记为已被访问
        //因为该点可以进入,所以计数要算上这个点,从1开始加
        //递归调用,加上上/左/下/右四个方向发现的可达点数目
        count = 1 + movingCountCore(threshold, rows, cols,
                                    row - 1, col, visited)
                + movingCountCore(threshold, rows, cols,
                                  row, col - 1, visited)
                + movingCountCore(threshold, rows, cols,
                                  row + 1, col, visited)
                + movingCountCore(threshold, rows, cols,
                                  row, col + 1, visited);
    }
    //如果不能进入,自然返回0
    return count;//返回计数
}

// 参数:
//        threshold:      阈值,即题目中的k值
//        rows:           矩阵的行数
//        cols:           矩阵的列数
//        row:            当前行
//        col:            当前列
//        visited:        访问情况矩阵
// 返回值:
//        true表示能进入该方格,false表示不能
bool check(int threshold, int rows, int cols, int row, int col,
           bool* visited) {
    //行列坐标合法,且行列的位和之和不超过阈值,且该位置未访问过
    if(row >= 0 && row < rows && col >= 0 && col < cols
            && getDigitSum(row) + getDigitSum(col) <= threshold
            && !visited[row* cols + col])
        return true;//满足这些条件,就能进入这个方格
    return false;//否则不能进入
}

//输入正整数number,输出其各个数位之和
int getDigitSum(int number) {
    int sum = 0;
    while(number > 0) {
        sum += number % 10;//把最低位取出来加到总和里
        number /= 10;//去除最低位
    }
    return sum;
}

int main() {
    cout<<movingCount(15,20,20)<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/82381848