回溯法
剑指offer —66题 矩阵中的路径
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
代码如下
和原来讲过的一道题目一样,只不过输入稍微改了一下,这里可以用原来的模板。
```
class Solution {
private:
vector<vector<bool>>visited;
int m,n;
int d[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
bool Area(int x,int y)
{
return x>=0&&x<m&&y>=0&&y<n;
}
bool help(char* board, char* word,int index,int x,int y)
{
if(index==strlen(word)-1)
return board[x*n+y]==word[index];
if(board[x*n+y]==word[index])
{
visited[x][y]=true;
for(int i=0;i<4;i++)
{
int newx=x+d[i][0];
int newy=y+d[i][1];
if(Area(newx,newy)&& !visited[newx][newy]&&help(board,word,index+1,newx,newy))
return true;
}
visited[x][y]=false;
}
return false;
}
public:
bool hasPath(char* matrix, int rows, int cols, char* str){
m=rows;
if(m<=0)
return false;
n=cols;
visited=vector<vector<bool>>(m,vector<bool>(n,false));
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(help(matrix,str,0,i,j))
return true;
return false;
}
};
```
剑指offer—67题 机器人运动范围
题目描述:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
代码如下:
主要分析:这个题和二维平面上原来做的回溯题一样,要灵活的设计函数的返回值,函数的形式可以根据实际情况设计,
这里的help函数就是设计成了void形式,而res设置成了一个全局变量,这样就可以方便的进行统计,一定注意不要重复加。
```
class Solution {
private:
vector<vector<bool>>visited;
int m,n;
int d[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
bool Area(int x,int y)
{
return x>=0&&x<m&&y>=0&&y<n;
}
bool Is_insert(int threshold,int x,int y)
{
int x_sum=0,y_sum=0;
while(x>0)
{
x_sum+=x%10;
x=x/10;
}
while(y>0)
{
y_sum+=y%10;
y=y/10;
}
if(x_sum+y_sum<=threshold)
return true;
return false;
}
int res=0;
void help(int threshold,int x,int y)
{
if(Is_insert(threshold,x,y)&&Area(x,y)&&!visited[x][y])
{
visited[x][y]=true;
res++;
//res=1+help(threshold,x-1,y)+help(threshold,x,y-1)+help(threshold,x+1,y)+help(threshold,x,y+1);
for(int i=0;i<4;i++)
{
int newx=x+d[i][0];
int newy=y+d[i][1];
help(threshold,newx,newy);
}
}
return ;
}
public:
int movingCount(int threshold, int rows, int cols)
{
if(rows<=0||threshold<0||cols<0)
return 0;
m=rows;
n=cols;
visited=vector<vector<bool>>(m,vector<bool>(n,false));
help(threshold,0,0);
return res;
}
};
```
##### 也可以这样写,辅助函数;这样写的好处是,如果让统计每一块连通域的个数的时候,非常方便,res是一个局部变量,容易控制。可以仿照着LeetCode中的洪水的那个题,去提升,应该会不错。
```
//int res=0;
void help(int threshold,int x,int y,int &res)
{
if(Is_insert(threshold,x,y)&&Area(x,y)&&!visited[x][y])
{
visited[x][y]=true;
res++;
//res=1+help(threshold,x-1,y)+help(threshold,x,y-1)+help(threshold,x+1,y)+help(threshold,x,y+1);
for(int i=0;i<4;i++)
{
int newx=x+d[i][0];
int newy=y+d[i][1];
help(threshold,newx,newy,res);
}
}
return ;
}
```
2017年软红笔试题(这部分来自网上内容整理)
题目: 给定一个mXn的矩阵,如下图所示,里面所有元素都非负,试找出一条从左上角到右下角的一条路径,要求路径上的数字之和最小。注:在某一个位置只有两种选择,向下或者向右。
“`
1 4 8 2 9
1 4 6 7 8
1 1 1 1 1
在以上矩阵中,其中数字之和最小的路径为1-1-1-1-1-1-1,其最小和为7.
请给出你的解题思路,并完成int min_sum_path(int *mat, int row, int col)。其中函数返回值为路径和的最小值,最后给出你算法的时间复杂度。
“`
思路一:
可直接用回溯算法,每次从当前位置有2种选择,要么向右,要么向下,示例代码如下:`
``
#include <iostream>
#include<string>
#include<vector>
using namespace std;
//存放位置
int POSX[10];
int POSY[10];
void DFS(int depth, int i, int j, int *mat, int m, int n, int &cursum, int &minsum)
{
//记录位置
POSX[depth] = i;
POSY[depth] = j;
//迭代结束
if (depth == n + m - 1)
{
return;
}
//更新最小值
if (i == m - 1 && j == n - 1 && cursum < minsum)
minsum = cursum;
//右
if (i<m && j + 1<n)
{
cursum += mat[i*n + (j + 1)];
DFS(depth + 1, i, j + 1, mat, m, n, cursum, minsum);
cursum -= mat[i*n + (j + 1)];
}
//下
if (i + 1<m && j<n)
{
cursum += mat[(i + 1)*n + j];
DFS(depth + 1, i + 1, j, mat, m, n, cursum, minsum);
cursum -= mat[(i + 1)*n + j];
}
}
int min_sum_path(int *mat, int row, int col)
{
int cursum = mat[0];
int minsum = 10000;
//求最值
DFS(0, 0, 0, mat, row, col, cursum, minsum);
return minsum;
}
int main()
{
int mat[] = { 1, 4, 8, 2, 9, 1, 4, 6, 7, 8, 1, 1, 1, 1, 1 };
int row = 3;
int col = 5;
int answer = min_sum_path(mat, row, col);
cout << "answer = " << answer << endl;
//输出位置
for (int i = 0; i<row + col - 1; i++)
{
cout << mat[POSX[i]* col + POSY[i]] << " ";
}
cout << endl;
return 0;
}
```
思路二:
动态规划,将该一维数组视为二维数组,由于每个位置只能往右或者往下走,不妨记F(i,j)表示从起点到位置(i,j)处的路径和的最小值,则状态转移方程为:
F(i+1,j+1) = min(F(i,j+1), F(i+1,j)) + mat[i+1][j+1] 其中 0< i + 1<row 0< j+1<col
初始条件为,对于第一行的位置,因为只能向右到达 ,所以 F(0,j) = sum(0,j) j=0…col; 对于第一列的位置,只能从起点向下到达,所以 F(i,0) = sum(i,0) i = 0…row
据此,可得代码如下:
```
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int min_sum_path(int *mat, int row, int col)
{
int i, j;
int dp[100][100] = { 0 };
dp[0][0] = mat[0];
//第一行
for ( j = 1; j < col; j++)
dp[0][j] = dp[0][j - 1] + mat[j];
//第一列
for ( i = 1; i < row; i++)
dp[i][0] = dp[i - 1][0] + mat[i*col];
//其他位置
for ( i = 1; i < row;i++)
for (j = 1; j < col; j++)
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + mat[i*col + j];
return dp[row-1][col-1];
}
int main()
{
int mat[] = { 1, 4, 8, 2, 9, 1, 4, 6, 7, 8, 1, 1, 1, 1, 1 };
int row = 3;
int col = 5;
int answer = min_sum_path(mat, row, col);
cout << "answer = " << answer << endl;
return 0;
}
```