回溯法:
回溯法可以视作蛮力法的升级版,他可以从解决问题的每一步的所有可能选项里系统的选择一个可行方案。所以回溯法适用于有多个步骤组成的问题。
题目一:
矩阵中的路径:判断一个矩阵中是否包含一条某字符串中所有字符的路径。路径可以从矩阵的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了某一格,则不能再进入该格子。
分析:
1. 可以任选一个格子作为起点
2, 矩阵边界上的点相邻点小于4
代码实现:
bool hasPathCore(const char* matrix, int rows, int cols, int row, int col, char *str, int &pathLength,bool* visited) //注意:int &pathLength,为引用
{
if (str[pathLength] == '\0') //目标str已经全部找到了
return true;
bool hasPath = false;
if (row >= 0 && row < rows && col >= 0 && col < cols && //确保在数组范围内,因为边沿处每个点不足4个方向
matrix[row*cols + col] == str[pathLength] && // 输入进来的此点匹配到了
!visited[row*cols + col]) //输入进来的此点可以访问
{
++pathLength; //点 可访问 且 点匹配到了,那么目标str就搞定了一个,++pathLength
visited[row*cols + col] = true; //点 可访问 且 点匹配到了,那么对应标记就置为true,表示路径上已经有此节点
hasPath = hasPathCore(matrix, rows, cols, row, col-1, str, pathLength, visited) // row, col-1 表示向下
|| hasPathCore(matrix, rows, cols, row-1, col, str, pathLength, visited) // row-1, col 表示向左
|| hasPathCore(matrix, rows, cols, row, col+1, str, pathLength, visited) // row, col+1 表示向上
|| hasPathCore(matrix, rows, cols, row+1, col, str, pathLength, visited);// row+1, col 表示向右
if (!hasPath) //表示此点之后的4个方向里找不到后续的str的匹配项了,例如该点为a,下一个为b,但a的四周都没有b
{
--pathLength; //不能继续下去,回退,str的匹配长度减一
visited[row*cols + col] = false; //不能继续下去,回退,此点不是路径上的点(可被再访问)
}
}
return hasPath;
}
bool hsaPath(char *matrix, int rows, int cols, char *str) //不用传str长度,str结尾的\0可用作 hasPathCore的结束条件
{
if (matrix == NULL || str == NULL || cols <= 0 || rows <= 0)
return false;
bool *visited = new bool[rows*cols]; //辅助数组,如果一条路径已经经过对应点,则置为true
memset(visited, 0, rows*cols);
int pathLength = 0; //标记访问了str多少个,第一次从str[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))
return true;
}
}
delete[] visited;
return false;
}
int main()
{
char matrix[] = { 'a', 'b', 't', 'd', 'c', 'f', 'c', 's', 'j', 'd', 'e', 'h' };
char *str = "bfce";
//char *str = "abfb";
bool hasPath = hsaPath(matrix, 3, 4, str);
cout << hasPath << endl;
}
题目二:
机器人的运动范围:地上有一个m行n列的方格。机器人从坐标(0,0)开始移动,它每次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标位数之和大于k的格子。例如 k=18,机器人可以进入(35,37),因为3+5+3+7=18,但它不能进入(35,38)
分析:
如果机器人能进入(i,j) 则再判断是否进入相邻的4个格子: (i,j-1)、(i-1,j) 、(i+1,j)、(i,j+1)
代码实现:
int getDigitSum(int number)
{
int sum = 0;
while (number > 0)
{
sum += number % 10;
number /= 10;
}
return sum;
}
bool check(int threshold, int rows, int cols, int row, int col, bool* visited) //判断是否可以进入此方格
{
if (row >= 0 && row < rows && col >= 0 && col < cols && //不越界
!visited[row*cols + col] && //节点还没访问过
getDigitSum(row) + getDigitSum(col) <= threshold) //给数位值和小于 k
{
return true;
}
return false;
}
int movingCountCore(int threshold, int rows, int cols, int row, int col, bool *visited)
{
int count = 0;
if (check(threshold, rows, cols, row, col, visited))
{
visited[row*cols + col] = true;
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); //下
}
return count;
}
int movingCount(int threshold, int rows, int cols) //返回可以去的位置的个数
{
//threshold就是那个k 值
if (threshold < 0 || rows <= 0 || cols <= 0)
return 0;
bool *visited = new bool[rows*cols];
for (int i = 0; i < rows*cols; i++)
visited[i] = false;
int count = movingCountCore(threshold, rows, cols, 0, 0, visited);
delete[] visited;
return count;
}
int main()
{
cout << movingCount(9, 5, 5); //5*5的方格,k值为9
}