当前位置: 首页 > news >正文

代码随想录|图论|10水流问题

leetcode:103. 水流问题

题目

现有一个 N × M 的矩阵,每个单元格包含一个数值,这个数值代表该位置的相对高度。

矩阵的左边界和上边界被认为是第一组边界,而矩阵的右边界和下边界被视为第二组边界。

矩阵模拟了一个地形,当雨水落在上面时,水会根据地形的倾斜向低处流动,但只能从较高或等高的地点流向较低或等高并且相邻(上下左右方向)的地点。我们的目标是确定那些单元格,从这些单元格出发的水可以达到第一组边界和第二组边界。

输入描述:

第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。

后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。

输出描述:

输出共有多行,每行输出两个整数,用一个空格隔开,表示可达第一组边界和第二组边界的单元格的坐标,输出顺序任意。

思路

暴力解法

就是遍历图中的每一个节点,用dfs或者bfs判断,从这个点扩散,是否可以到达第一组边界和第二组边界。

#include <bits/stdc++.h>
using namespace std;int n, m;
int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};void dfs(vector<vector<int>> &grid, vector<vector<bool>> &visited, int x, int y)
{// 终止条件if (visited[x][y])return;visited[x][y] = true;for (int i = 0; i < 4; i++){int nextx = x + dir[i][0];int nexty = y + dir[i][1];if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size())continue;if (grid[x][y] < grid[nextx][nexty])continue; // 高度不合适dfs(grid, visited, nextx, nexty);}
}bool isResult(vector<vector<int>> &grid, int x, int y)
{vector<vector<bool>> visited(n, vector<bool>(m, false));// 将从x y出发可以到达的所有节点标记上dfs(grid, visited, x, y);bool isFirst = false;bool isSecond = false;// 判断从x,y出发,是否可以到达第一组边界和第二组边界for (int j = 0; j < m; j++){if (visited[0][j]){isFirst = true;break;}}for (int i = 0; i < n; i++){if (visited[i][0]){isFirst = true;break;}}for (int j = 0; j < m; j++){if (visited[n - 1][j]){isSecond = true;break;}}for (int i = 0; i < n; i++){if (visited[i][m - 1]){isSecond = true;break;}}if (isFirst && isSecond)return true;return false;
}int main()
{cin >> n >> m;vector<vector<int>> grid(n, vector<int>(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++){for (int j = 0; j < m; j++){if (isResult(grid, i, j))cout << i << " " << j << endl;}}return 0;
}

dfs里面需要注意的是

if (grid[x][y] < grid[nextx][nexty])continue; // 高度不合适

因为水流只能往高度低的地方流动。

暴力解法用了n*m次dfs,复杂度是非常高的O(m^2 * n^2)。

逆向解法

一个点出发,要能到达两个地方,反过来想就是,两个地方出发,如果都经过这个点,那么这个点就是我们需要的。

所以dfs只要从第一组边界来一趟,再从第二组边界来一趟,他们的交集,就是最终答案,如图:

代码:

// ====================================== 逆向======================================
#include <bits/stdc++.h>
using namespace std;int n, m;
int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};// dfs逆向流水,将可以逆向流到的节点进行标记
void dfs(vector<vector<int>> &grid, vector<vector<bool>> &visited, int x, int y)
{// 终止条件if (visited[x][y])return;// 处理当前节点visited[x][y] = true;// 处理相邻节点for (int i = 0; i < 4; i++){int nextx = x + dir[i][0];int nexty = y + dir[i][1];if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size())continue;// 逆流向上的条件是下一个点要 >= 当前点,所以先把不满足的情况排除掉,再进行dfsif (grid[x][y] > grid[nextx][nexty])continue;dfs(grid, visited, nextx, nexty);}
}int main()
{cin >> n >> m;vector<vector<int>> grid(n, vector<int>(m, 0));for (int i = 0; i < n; i++){for (int j = 0; j < m; j++){cin >> grid[i][j];}}// 标记从第一组边界开始出发,可以到达的节点。vector<vector<bool>> first_vis(n, vector<bool>(m, false));// 标记从第二组边界开始出发,可以到达的节点。vector<vector<bool>> second_vis(n, vector<bool>(m, false));// 最左列(第一组边界)、最右列(第二组边界)for (int i = 0; i < n; i++){dfs(grid, first_vis, i, 0);dfs(grid, second_vis, i, m - 1);}// 最上行(第一组边界)、最下行(第二组边界)for (int j = 0; j < m; j++){dfs(grid, first_vis, 0, j);dfs(grid, second_vis, n - 1, j);}// 如果某个节点是第一组边界出发和第二组边界出发都可以到达的点for (int i = 0; i < n; i++){for (int j = 0; j < m; j++){if (first_vis[i][j] == true && second_vis[i][j] == true){cout << i << " " << j << endl;}}}return 0;
}

 逆向求解跟暴力求解的区别:

  1. dfs里面的水流流向判断条件反过来。
  2. 暴力求解是对每一个点都使用dfs,逆向求解只对第一组边、第二组边使用dfs。

总结

谁能想到?

参考资料

103. 水流问题 

http://www.xdnf.cn/news/1104499.html

相关文章:

  • 项目捷报 | 冠捷科技泰国工厂THA MES项目成功验收!TPV国际化布局再添里程碑!
  • 机器学习之线性回归(七)
  • 【unitrix】 4.20 类型级二进制数减法实现解析(sub.rs)
  • C++ auto与 for循环
  • 玖玖NFT数字藏品源码(源码下载)
  • Adobe Acrobat DC JavaScript 基础到应用
  • c++STL-优先队列priority_queue和仿函数
  • Docker高级管理--Dockerfile 镜像制作
  • 伺服驱动控制CANopen协议
  • 弧焊机器人气体全方位节能指南
  • Shein在欧又遭针对?从4000万欧到1.5亿欧,Shein两个月内连收两张法国罚单!
  • TCP详解——流量控制、滑动窗口
  • 【Linux】系统引导修复
  • [精选]如何解决pip安装报错ModuleNotFoundError: No module named ‘subprocess’问题
  • C++设计秘籍:为什么所有参数都需类型转换时,非成员函数才是王道?
  • V少JS基础班之第七弹
  • 从一到无穷大 #47:浅谈对象存储加速
  • 自动驾驶线控系统与动力电池系统
  • 基于MuJoCo的宇树科技G1机器人基础动作仿真研究
  • BLE低功耗设计:从广播模式到连接参数优化的全链路分析与真题解析
  • 2025 年第十五届 APMCM 亚太地区大学生数学建模竞赛-A题 农业灌溉系统优化
  • DOM编程实例(不重要,可忽略)
  • Telegraf vs. Logstash:实时数据处理架构中的关键组件对比
  • 【数据结构与算法】206.反转链表(LeetCode)
  • 麦迪逊悬架cad【14张】+三维图+设计说明书
  • 基于生产者消费者模型的线程池【Linux操作系统】
  • 《PyQtGraph:Python绘图领域的“超级引擎”》
  • 加工进化论:SPL 一键加速日志转指标
  • Genus:设计信息结构以及导航方式(路径种类)
  • php use 命名空间与 spl_autoload_register的关系