103. 水流问题-图论

https://kamacoder.com/problempage.php?pid=1175
题目描述:

现有一个 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
提示信息:
图中的蓝色方块上的雨水既能流向第一组边界,也能流向第二组边界。所以最终答案为所有蓝色方块的坐标。
数据范围:
1 <= M, N <= 50

思路

一个比较直白的想法,其实就是 遍历每个点,然后看这个点 能不能同时到达第一组边界和第二组边界。
至于遍历方式,可以用dfs,也可以用bfs,以下用dfs来举例。

那么我们可以 反过来想,从第一组边界上的节点 逆流而上,将遍历过的节点都标记上。
同样从第二组边界的边上节点 逆流而上,将遍历过的节点也标记上。
然后两方都标记过的节点就是既可以流向第一组边界也可以流向第二组边界的节点。
从第一组边界边上节点出发,如图: (图中并没有把所有遍历的方向都画出来,只画关键)

20250304174747

从第二组边界上节点出发,如图: (图中并没有把所有遍历的方向都画出来,只画关键部分)

20250304174801

public class Main {

// 采用 DFS 进行搜索
public static void dfs(int[][] heights, int x, int y, boolean[][] visited, int preH) {
// 遇到边界或者访问过的点,直接返回
if (x < 0 || x >= heights.length || y < 0 || y >= heights[0].length || visited[x][y]) return;
// 不满足水流入条件的直接返回
if (heights[x][y] < preH) return;
// 满足条件,设置为true,表示可以从边界到达此位置
visited[x][y] = true;

// 向下一层继续搜索
dfs(heights, x + 1, y, visited, heights[x][y]);
dfs(heights, x - 1, y, visited, heights[x][y]);
dfs(heights, x, y + 1, visited, heights[x][y]);
dfs(heights, x, y - 1, visited, heights[x][y]);
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();

int[][] heights = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
heights[i][j] = sc.nextInt();
}
}

// 初始化两个二位boolean数组,代表两个边界
boolean[][] pacific = new boolean[m][n];
boolean[][] atlantic = new boolean[m][n];

// 从左右边界出发进行DFS
for (int i = 0; i < m; i++) {
dfs(heights, i, 0, pacific, Integer.MIN_VALUE);
dfs(heights, i, n - 1, atlantic, Integer.MIN_VALUE);
}

// 从上下边界出发进行DFS
for (int j = 0; j < n; j++) {
dfs(heights, 0, j, pacific, Integer.MIN_VALUE);
dfs(heights, m - 1, j, atlantic, Integer.MIN_VALUE);
}

// 当两个边界二维数组在某个位置都为true时,符合题目要求
List<List> res = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (pacific[i][j] && atlantic[i][j]) {
res.add(Arrays.asList(i, j));
}
}
}

// 打印结果
for (List list : res) {
for (int k = 0; k < list.size(); k++) {
if (k == 0) {
System.out.print(list.get(k) + " ");
} else {
System.out.print(list.get(k));
}
}
System.out.println();
}
}
}

posted @ 2026-03-28 10:08  大头海绵宝宝  阅读(9)  评论(0)    收藏  举报