题目一:被围绕的区域(中等)
给你一个 m x n
的矩阵 board
,由若干字符 'X'
和 'O'
,找到所有被 'X'
围绕的区域,并将这些区域里所有的 'O'
用 'X'
填充。
示例 1:
输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]] 输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]] 解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的'O'
都不会被填充为'X'
。 任何不在边界上,或不与边界上的'O'
相连的'O'
最终都会被填充为'X'
。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:
输入:board = [["X"]] 输出:[["X"]]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j]
为'X'
或'O'
题目分析
思路一
因为我们不确定哪一块的区域与边缘的'O'相邻,那么,我们可以使用一个和board相同的数组去尝试,如果记录的标记结果最后表示这一块区域与边缘'O'相邻,那就用board重置尝试数组,否则将尝试的数组赋值给board。
class Solution
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
vector<vector<bool>> isused;
vector<vector<char>> copyboard;//尝试数组
bool isside;//进行标记,表示该区域是否与边缘'O'相邻。
public:
void solve(vector<vector<char>>& board)
{
m=board.size(),n=board[0].size();
isused=vector<vector<bool>>(m,vector<bool>(n,false));
copyboard=board;
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(isused[i][j]==false&&board[i][j]=='O')
{
isside=false;//每进入一块区域时,需要重置标记
solvedfs(i,j);
if(isside==true)//表示相邻,说明board不能改变当前区域,重置copyboard
{
copyboard=board;
}
else//表示该区域被'X'完全包围,将copyboard赋值给board
{
board=copyboard;
}
}
}
}
}
void solvedfs(int x,int y)
{
copyboard[x][y]='X';
isused[x][y]=true;
if(x==0||y==0||x==m-1||y==n-1)//记录是否与边缘'O'相邻
{
isside=true;
}
for(size_t i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&isused[newx][newy]==false&©board[newx][newy]=='O')
{
solvedfs(newx,newy);
}
}
}
};
思路二
class Solution
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
vector<vector<bool>> isused;
public:
void solve(vector<vector<char>>& board)
{
m=board.size(),n=board[0].size();
isused=vector<vector<bool>>(m,vector<bool>(n,false));
//对边缘的连接'O'区域标记为'K'
for(size_t i=0;i<m;i++)
{
if(isused[i][0]==false&&board[i][0]=='O') solvedfs(board,i,0,'K');
if(isused[i][n-1]==false&&board[i][n-1]=='O') solvedfs(board,i,n-1,'K');
}
for(size_t j=0;j<n;j++)
{
if(isused[0][j]==false&&board[0][j]=='O') solvedfs(board,0,j,'K');
if(isused[m-1][j]==false&&board[m-1][j]=='O') solvedfs(board,m-1,j,'K');
}
//对全部被'X'包围的区域修改为'X'
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(isused[i][j]==false&&board[i][j]=='O')
{
solvedfs(board,i,j,'X');
}
}
}
//将标记区域进行还原
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(board[i][j]=='K')
{
board[i][j]='O';
}
}
}
}
void solvedfs(vector<vector<char>>& board,int x,int y,char tmp)
{
board[x][y]=tmp;
isused[x][y]=true;
for(size_t i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&isused[newx][newy]==false&&board[newx][newy]=='O')
{
solvedfs(board,newx,newy,tmp);
}
}
}
};
题目二:太平洋大西洋水流问题(中等)
417. 太平洋大西洋水流问题 - 力扣(LeetCode)
有一个 m × n
的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。
这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n
的整数矩阵 heights
, heights[r][c]
表示坐标 (r, c)
上单元格 高于海平面的高度 。
岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。
返回网格坐标 result
的 2D 列表 ,其中 result[i] = [ri, ci]
表示雨水从单元格 (ri, ci)
流动 既可流向太平洋也可流向大西洋 。
示例 1:
输入: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]] 输出: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
示例 2:
输入: heights = [[2,1],[1,2]] 输出: [[0,0],[0,1],[1,0],[1,1]]
提示:
m == heights.length
n == heights[r].length
1 <= m, n <= 200
0 <= heights[r][c] <= 105
题目分析
class Solution
{
vector<vector<int>> result;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<int> path;//记录下标的工具,方便将下标传入result。
int m,n;
public:
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights)
{
m=heights.size(),n=heights[0].size();
path=vector<int>(2);
vector<vector<bool>> recordpacific(m,vector<bool>(n,false));//表示该点的水可以流向pacific
vector<vector<bool>> recordAtlantic(m,vector<bool>(n,false));//表示该点的水可以流向Atlantic
dfs(heights,0,0,recordpacific,0,0);//对可以流向太平洋的点进行标记,
dfs(heights,m-1,n-1,recordAtlantic,m-1,n-1);//对可以流向大西洋的点进行标记
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(recordpacific[i][j]==true&&recordAtlantic[i][j]==true)
{
path[0]=i;
path[1]=j;
result.push_back(path);
}
}
}
return result;
}
//x,y:表示的是所需查看的下标点,而boundaryx和boundaryy表示的是与对应海洋贴合的边界
void dfs(vector<vector<int>>& heights,int x,int y,vector<vector<bool>>& record,int boundaryx,int boundaryy)
{
record[x][y]=true;
for(size_t i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&record[newx][newy]==false&&
(heights[newx][newy]>=heights[x][y]||newx==boundaryx||newy==boundaryy))
{
dfs(heights,newx,newy,record,boundaryx,boundaryy);
}
}
}
};
题目三:扫雷(中等)
让我们一起来玩扫雷游戏!
给你一个大小为 m x n
二维字符矩阵 board
,表示扫雷游戏的盘面,其中:
'M'
代表一个 未挖出的 地雷,'E'
代表一个 未挖出的 空方块,'B'
代表没有相邻(上,下,左,右,和所有4个对角线)地雷的 已挖出的 空白方块,- 数字(
'1'
到'8'
)表示有多少地雷与这块 已挖出的 方块相邻, 'X'
则表示一个 已挖出的 地雷。
给你一个整数数组 click
,其中 click = [clickr, clickc]
表示在所有 未挖出的 方块('M'
或者 'E'
)中的下一个点击位置(clickr
是行下标,clickc
是列下标)。
根据以下规则,返回相应位置被点击后对应的盘面:
- 如果一个地雷(
'M'
)被挖出,游戏就结束了- 把它改为'X'
。 - 如果一个 没有相邻地雷 的空方块(
'E'
)被挖出,修改它为('B'
),并且所有和其相邻的 未挖出 方块都应该被递归地揭露。 - 如果一个 至少与一个地雷相邻 的空方块(
'E'
)被挖出,修改它为数字('1'
到'8'
),表示相邻地雷的数量。 - 如果在此次点击中,若无更多方块可被揭露,则返回盘面。
示例 1:
输入:board = [["E","E","E","E","E"],["E","E","M","E","E"],["E","E","E","E","E"],["E","E","E","E","E"]], click = [3,0] 输出:[["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]]
示例 2:
输入:board = [["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]], click = [1,2] 输出:[["B","1","E","1","B"],["B","1","X","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 50
board[i][j]
为'M'
、'E'
、'B'
或数字'1'
到'8'
中的一个click.length == 2
0 <= clickr < m
0 <= clickc < n
board[clickr][clickc]
为'M'
或'E'
题目分析
class Solution
{
//表示当前下标附近的八个位置的下标
int dx[8]={0,0,1,-1,-1,1,1,-1};
int dy[8]={1,-1,0,0,1,1,-1,-1};
vector<vector<bool>> isused;
int m,n;
public:
vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click)
{
if(board[click[0]][click[1]]=='M')
{
board[click[0]][click[1]]='X';
return board;
}
m=board.size(),n=board[0].size();
isused=vector<vector<bool>>(m,vector<bool>(n,false));
updateBoarddfs(board,click[0],click[1]);
return board;
}
void updateBoarddfs(vector<vector<char>>& board,int x,int y)
{
//搜索查看当前的下标点附近的八个位置有几个地雷
int count=0;
for(size_t i=0;i<8;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&board[newx][newy]=='M')
{
count++;
}
}
//如果存在地雷,那么就会将当前下标修改为附近地雷,同时可以设置返回条件,以防止对当前下标继续对旁边进行递归
if(count)
{
board[x][y]='0'+count;
return;
}
else
{
board[x][y]='B';
for(size_t i=0;i<8;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&board[newx][newy]=='E')
{
updateBoarddfs(board,newx,newy);
}
}
}
}
};
题目四:黄金矿工(中等)
你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n
的网格 grid
进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0
。
为了使收益最大化,矿工需要按以下规则来开采黄金:
- 每当矿工进入一个单元,就会收集该单元格中的所有黄金。
- 矿工每次可以从当前位置向上下左右四个方向走。
- 每个单元格只能被开采(进入)一次。
- 不得开采(进入)黄金数目为
0
的单元格。 - 矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。
示例 1:
输入:grid = [[0,6,0],[5,8,7],[0,9,0]] 输出:24 解释: [[0,6,0], [5,8,7], [0,9,0]] 一种收集最多黄金的路线是:9 -> 8 -> 7。
示例 2:
输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]] 输出:28 解释: [[1,0,7], [2,0,6], [3,4,5], [0,3,0], [9,0,20]] 一种收集最多黄金的路线是:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7。
提示:
1 <= grid.length, grid[i].length <= 15
0 <= grid[i][j] <= 100
- 最多 25 个单元格中有黄金。
题目分析
class Solution
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<vector<bool>> isused;
int maxgold;
int m,n;
public:
int getMaximumGold(vector<vector<int>>& grid)
{
m=grid.size(),n=grid[0].size();
isused=vector<vector<bool>>(m,vector<bool>(n,false));
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(grid[i][j]==0) continue;
isused[i][j]=true;
getMaximumGolddfs(grid,i,j,grid[i][j]);
isused[i][j]=false;
}
}
return maxgold;
}
//对于需要对递归的每一条路径都进行计数的递归函数,将计数作为参数传参可以确保正常计算,同时
//可以避免需要自己恢复现场而可能出现的失误,因此用pathsum作为参数
void getMaximumGolddfs(vector<vector<int>>& grid,int x,int y,int pathsum)
{
for(size_t i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&isused[newx][newy]==false&&grid[newx][newy]!=0)
{
isused[newx][newy]=true;
getMaximumGolddfs(grid,newx,newy,pathsum+grid[newx][newy]);
isused[newx][newy]=false;
}
}
maxgold=max(maxgold,pathsum);
}
};
题目五:不同路径三(困难)
在二维网格 grid
上,有 4 种类型的方格:
1
表示起始方格。且只有一个起始方格。2
表示结束方格,且只有一个结束方格。0
表示我们可以走过的空方格。-1
表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。
每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
示例 1:
输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
示例 2:
输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] 输出:4 解释:我们有以下四条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
示例 3:
输入:[[0,1],[2,0]] 输出:0 解释: 没有一条路能完全穿过每一个空的方格一次。 请注意,起始和结束方格可以位于网格中的任意位置。
提示:
1 <= grid.length * grid[0].length <= 20
题目分析
读题我们可以发现完成该题目必须满足以下几个条件:
1、不能连接到-1的节点。
2、必须从数为1的节点开始。
3、终点的数必须是2.
4、除了-1的节点,其余所有的节点必须”一笔画“连接完。
读题结论
得出:(1)需要知道节点为1的下标,(2)需要知道除了-1以外一共有多少节点,设为step
(3)深搜的递归结束条件是:遍历到节点为2时,递归结束。这时候分俩种情况:
(一)当恰好已经遍历了step个节点,该节点为2恰好是最后一个节点,说明该条路径正确
(二)还未遍历了step个节点,该路径错误。
结果:递归结束。
class Solution
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<vector<bool>> isused;
int m,n;
int startx,starty;
int step,count;
public:
int uniquePathsIII(vector<vector<int>>& grid)
{
m=grid.size(),n=grid[0].size();
isused=vector<vector<bool>>(m,vector<bool>(n,false));
//求出起始点下标,以及除了-1以外有多少节点
for(size_t i=0;i<m;i++)
{
for(size_t j=0;j<n;j++)
{
if(grid[i][j]!=-1) step++;
if(grid[i][j]==1) startx=i,starty=j;
}
}
isused[startx][starty]=true;
uniquePathsIIIdfs(grid,startx,starty,1);
return count;
}
void uniquePathsIIIdfs(vector<vector<int>>& grid,int x,int y,int pathstep)
{
//递归结束条件
if(grid[x][y]==2)
{
if(pathstep==step) count++;
return;
}
for(size_t i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<m&&newy>=0&&newy<n&&isused[newx][newy]==false&&grid[newx][newy]!=-1)
{
isused[newx][newy]=true;
uniquePathsIIIdfs(grid,newx,newy,pathstep+1);
isused[newx][newy]=false;
}
}
}
};