版权声明:本文为博主原创学习笔记,如需转载请注明来源。 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;
}