当前位置: 首页 > 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.lryc.cn/news/584893.html

相关文章:

  • 视频人脸处理——人脸面部动作提取
  • 静电式 vs UV 光解:哪种油烟净化技术更适合你的餐厅?
  • python的病例管理系统
  • 【JMeter】执行系统命令
  • VS 按F12 提示cannot navigate to the symbol under the caret
  • 机器学习详解
  • linux中INIT_MM_CONTEXT宏对pgd的重复赋值
  • Windows 10 2021 LTSC【版本号:19044.6036】
  • 设计模式笔记_结构型_代理模式
  • 小白学Python,标准库篇——随机库、正则表达式库
  • 【跟着PMP学习项目管理】每日一练 - 5
  • C++,从汇编角度看《虚拟继承的邪恶》
  • 【Linux】GDB/CGDB 调试器学习笔记
  • 【经典面经】C++新特性 TCP完整收发数据 TLS1.2 TLS1.3
  • AWS控制台升级EKS版本
  • AI进化论07:第二次AI寒冬——AI“改头换面”,从“AI”变成“机器学习”
  • 学习C++、QT---20(C++的常用的4种信号与槽、自定义信号与槽的讲解)
  • 基于vscode开发工具显示git提交信息的插件
  • Web3.0 支付网络对企业的优势
  • Linux磁盘限速(Ubuntu24实测)
  • spark3 streaming 读kafka写es
  • 可以悬浮在Windows电脑桌面的好用便签软件评测
  • 前端开发—全栈开发
  • php use 命名空间与 spl_autoload_register的关系
  • DVWA靶场通关笔记-反射型XSS(Reflected Low级别)
  • uni-app获取手机当前连接的WIFI名称
  • 小皮面板搭建pikachu
  • 如何将文件从OPPO手机传输到电脑
  • GNhao,获取跨境手机SIM卡跨境通信新选择!
  • 手机恢复出厂设置怎么找回数据?Aiseesoft FoneLab for Android数据恢复工具分享