综合练习四
- 1.单词搜索
- 2.黄金矿工
- 3.不同路径 III
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.单词搜索
题目链接:79. 单词搜索
题目分析:
给一个mxn board二维数组,让在数组中找是否存在字符串单词 word,注意只能上下左右去找,不能斜着找。
算法原理:
我们在这个矩阵中依次找到word里面的所有字符。首先在这个矩阵里面先一行一行的扫描找到word里面第一个字符,当找到第一个字符时,就从这个字符开始去匹配其他剩余的字符,注意只能上下左右去匹配,如果上下左右都不匹配说明这个位置是一次失败的匹配。那就在去找其他位置能匹配的第一个字符。如果找到还是上下左右去匹配,如果能匹配成功说明这个矩阵有这个单词。
如果把这道题抽象一下,你会发现刚刚的过程j就是解决迷宫常用的方式,深度优先遍历。如果走不通就回溯一下再往下走,直到找到一个正确结果为止。
接下来我们考虑递归函数如何设计,从某个节点开始上下左右匹配下一个字符,所以则函数体干的就是给一个位置然后上下左右去匹配下一个字符。这个参数首先把这个board给我,然后第一个字符的位置,然后给我要匹配字符串中下一个字符的位置。dfs(board,i,j,s,pos),注意看这个决策树我们从一个位置走可能走失败,上面调用dfs的得知道是找成功还是失败,所以dfs有一个bool类型的返回值,
bool dfs(board,i,j,s,pos) 失败就去另一条路径。剪枝就是那一个位置能匹配就去走那一个位置。回溯 一条路径找失败往上走就是回溯,往下走之前你弄了什么,反着来就可以了。
下面是细节问题:二维矩阵搜索中,经常要注意的细节。
不能走重复的路
就比如下面的这个位置,一直会是重复的。之前用过的下一次不能在找了。
这里我们有两种方法规避。
- 搞一个bool类型的跟原始数组大小一样的二维数组 bool visit[][]。用这个数组来标记当前这个位置是否被使用过。使用过就把这个位置标记成true。然后到下一层的时候就考虑一下上一个位置是否是ture,是就不走,不是就可以走。
- 修改原始矩阵的值。 比如说把被使用的位置的值修改成*,面试时还是要问一下能否修改,能修改再用这种方法。但是还有一个问题你修改完去往下一层,然后再回来的时候你要把被修改的值在还原回来。
这里在写代码去上下左右匹配的时候有两种写法:
- 可以分别写上下左右四个函数去匹配。
- 我们可以用向量的方式,定义上下左右四个位置。 然后仅需一次for循环就可以把上下左右都考虑进去了。
单独看这个i,上下左右就是在原始的i基础上要么不变,要么+1,要么-1,所以可以搞一个数组 int dx[4]={0,0,1,-1}; 这个表示i接下来可能的偏移量。同理j也有四个偏移量 int dy[4]={-1,1,0,0}; 然后这两个就可以上下凑一起使用。
class Solution {
bool check[7][7];
int m,n;
public:
bool exist(vector<vector<char>>& board, string word) {
m=board.size(),n=board[0].size();
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < n; ++j)
{
if(board[i][j] == word[0])
{
check[i][j]=true;
if(dfs(board,i,j,word,1)) return true;
check[i][j]=false;
}
}
}
return false;
}
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
bool dfs(vector<vector<char>>& board,int i,int j,string& s,int pos)
{
if(pos == s.size()) return true;
for(int k = 0; k < 4; ++k)
{
int x=i+dx[k],y=j+dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && check[x][y] == false && board[x][y] == s[pos])
{
check[x][y]=true;
if(dfs(board,x,y,s,pos+1)) return true;
check[x][y]=false;
}
}
return false;
}
};
2.黄金矿工
题目链接:1219. 黄金矿工
题目分析:
这道题和上面单词搜索就是同属于一类题,这里也是上下左右去搜索,不能走重复路。当天这里还有要求不能开采为0的单元格。
算法原理:
我们还是先去遍历找到不为0的单元格,然后从这个位置开始上下左右去递归,注意已经被选过的下次不能在选,可以弄一个原数组大小bool类型二维数组,当然也可以修改原始的值。其余的几乎和上面题一模一样。
修改原始的值
class Solution {
int m,n,ret;
public:
int getMaximumGold(vector<vector<int>>& grid) {
m=grid.size(),n=grid[0].size();
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < n ; ++j)
{
if(grid[i][j] != 0)
{
int num=grid[i][j];
grid[i][j]=0;
dfs(grid,i,j,num);
grid[i][j]=num;
}
}
}
return ret;
}
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
void dfs(vector<vector<int>>& grid,int i,int j,int path)
{
//每次递归进来就统计,不用硬找递归出口
ret=max(ret,path);
for(int k = 0; k < 4; ++k)
{
int x=i+dx[k],y=j+dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != 0)
{
int num=grid[x][y];
grid[x][y]=0;
dfs(grid,x,y,path+num);
grid[x][y]=num;
}
}
}
};
class Solution
{
bool vis[16][16];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int m, n;
int ret;
public:
int getMaximumGold(vector<vector<int>>& grid)
{
m = g.size(), n = g[0].size();
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
if(grid[i][j])
{
vis[i][j] = true;
dfs(grid, i, j, grid[i][j]);
vis[i][j] = false;
}
}
}
return ret;
}
void dfs(vector<vector<int>>& grid, int i, int j, int path)
{
ret = max(ret, path);
for(int k = 0; k < 4; k++)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y])
{
vis[x][y] = true;
dfs(grid, x, y, path + grid[x][y]);
vis[x][y] = false;
}
}
}
};
3.不同路径 III
题目链接:980. 不同路径 III
题目分析:
这个棋盘,1代表开始,2代表结束,0表示可以走过的方格,-1表示障碍。
从开始到结束有很多的走法,但是仅有两种路径把所有0方格都通过一次。
算法原理:
我们策略就是把从1走到2所有合法的路径个数统计出来。我们可以先扫描出数组找到起始位置,然后从这个起始位置开始dfs,不管你怎么走的,只要走到2你这条路径是合法的就行了。如何判断合法特别简单,向下递归的时候用一个count变量记录一下从开始到结束所走的这条路径个数。当走到2的时候判断一下这个count和我实际要走的步数step是否一样,实际要走步数在dfs之前把先描述整个数组先统计这个数组0有多少个,然后在加上起始位置和结束位置就是实际要走的步数。如果走到2 count == step 就找到一条路径。不相等就不统计。然后继续暴搜直到把所有能到2的路径合法的统计出来。
class Solution {
bool vis[21][21];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int m,n;
int ret,step;
public:
int uniquePathsIII(vector<vector<int>>& grid) {
m=grid.size(),n=grid[0].size();
int beginx,beginy;
// 统计0的个数 以及 开始的地方
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < n ; ++j)
{
if(grid[i][j] == 0) ++step;
else if(grid[i][j] == 1)
{
beginx=i;
beginy=j;
}
}
}
// 到底终点所需步数
step += 2;
vis[beginx][beginy]=true;
dfs(grid,beginx,beginy,1);
return ret;
}
void dfs(vector<vector<int>>& grid,int i,int j,int count)
{
if(grid[i][j] == 2)
{
if(count == step) //判断是否合法
++ret;
return;
}
for(int k = 0; k < 4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1)
{
vis[x][y]=true;
dfs(grid,x,y,count+1);
vis[x][y]=false;
}
}
}
};