- 第 164 篇 -
Date: 2026 - 02- 10 (周二)
Author: 郑龙浩(仟墨)
2026-02-10 周二 | 算法打卡day5
文章目录
-
- [2026-02-10 周二 | 算法打卡day5](#2026-02-10 周二 | 算法打卡day5)
7_卡码网103_高山流水
**题目描述
现有一个 N × M 的矩阵,每个单元格包含一个数值,这个数值代表该位置的相对高度。矩阵的左边界和上边界被认为是第一组边界,而矩阵的右边界和下边界被视为第二组边界。
矩阵模拟了一个地形,当雨水落在上面时,水会根据地形的倾斜向低处流动,但只能从较高或等高的地点流向较低或等高并且相邻(上下左右方向)的地点。我们的目标是确定那些单元格,从这些单元格出发的水可以达到第一组边界和第二组边界。
输入描述
第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。
后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。
输出描述
输出共有多行,每行输出两个整数,用一个空格隔开,表示可达第一组边界和第二组边界的单元格的坐标,输出顺序任意。
输入示例
5 5
1 3 1 2 4
1 2 1 3 2
2 4 7 2 1
4 5 6 1 1
1 4 1 2 1
输出示例
0 4
1 3
2 2
3 0
3 1
3 2
4 0
4 1
cpp
// Date: 2026-02-10 周一 Author:郑龙浩
// 7_卡码网103_高山流水_BFS_DFS
// 逆向思维思考:
// 也就是本身水是从中间向两边界流,现在是从两个边界往中间流,即从低往高流
// - 然后所有第一边界搜索的的坐标用first_visited数组去标记
// - 第二边界的用second_visited数组去标记
// 两者共同搜索过的坐标就是既可以到达第一也可以到到第二边界的坐标
// 方法1:DFS 方法2: BFS
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
void dfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
if (visited[x][y] == true) { // 要先判断传入的坐标是否是被访问过的,以免出现问题
return;
}
visited[x][y] = true; // 但凡传来的且没访问过的,都合格的,要标记访问过
int height = grid.size();
int width = grid[0].size();
for (int i = 0; i < 4; i++) {
int nextX = x + direction[i][0];
int nextY = y + direction[i][1];
// 只有 不越界 && 没访问过 && 下一个的坐标高度 > 当前坐标高度时,才会以下一个高度为中心搜索
if (nextX >= 0 && nextX < height && nextY >=0 && nextY < width && visited[nextX][nextY] == false && grid[nextX][nextY] >= grid[x][y]) {
dfs(grid, visited, nextX, nextY);
}
}
}
void bfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
if (visited[x][y] == true) {
return;
}
queue <pair <int, int>> que; // 待搜索的加入到这里面来
que.push({x, y});
int height = grid.size();
int width = grid[0].size();
// 每次都以队列头为中心搜索四周,如果找到符合条件的,就将坐标加入到que待访问的队列
while (!que.empty()) {
pair <int, int> cur = que.front(); // 取出队列头
que.pop(); // 只有取出队列的时候,才算访问过了
visited[cur.first][cur.second] = true;
for (int i = 0; i < 4; i++) {
int nextX = cur.first + direction[i][0];
int nextY = cur.second + direction[i][1];
// 只有 不越界 && 没访问过 && 下一个的坐标高度 > 当前坐标高度时,才会加入que这个待搜索队列
if (nextX >= 0 && nextX < height && nextY >=0 && nextY < width && visited[nextX][nextY] == false && grid[nextX][nextY] >= grid[cur.first][cur.second]) {
que.push({nextX, nextY});
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M; cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M, 0));
vector <vector <bool>> first_visited(N, vector <bool> (M, 0));
vector <vector <bool>> second_visited(N, vector <bool> (M, 0));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
// 处理左右两边(第一边界和第二边界)
for (int i = 0; i < N; i++) {
bfs(grid, first_visited, i, 0);
bfs(grid, second_visited, i, M - 1);
}
// 处理上下两行(第一边界和第二边界)
for (int i = 0; i < M; i++) {
bfs(grid, first_visited, 0, i);
bfs(grid, second_visited, N - 1, i);
}
// 遍历每一个点,看是否能同时到达第一组边界和第二组边界
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (first_visited[i][j] == true && second_visited[i][j] == true) {
cout << i << ' ' << j << '\n';
}
}
}
return 0;
}