'commit'
This commit is contained in:
154
TangDou/AcWing/FloodFill/1097.md
Normal file
154
TangDou/AcWing/FloodFill/1097.md
Normal file
@@ -0,0 +1,154 @@
|
||||
## [$AcWing$ $1097$. 池塘计数](https://www.acwing.com/problem/content/1099/)
|
||||
|
||||
### 一、题目描述
|
||||
农夫约翰有一片 $N∗M$ 的矩形土地。
|
||||
|
||||
最近,由于降雨的原因,部分土地被水淹没了。
|
||||
|
||||
现在用一个字符矩阵来表示他的土地。
|
||||
|
||||
每个单元格内,如果包含雨水,则用 $W$ 表示,如果不含雨水,则用 $.$表示。
|
||||
|
||||
现在,约翰想知道他的土地中形成了多少片池塘。
|
||||
|
||||
**每组相连的积水单元格集合可以看作是一片池塘**。
|
||||
|
||||
每个单元格视为与其 **上、下、左、右、左上、右上、左下、右下** 八个邻近单元格相连。
|
||||
|
||||
请你输出共有多少片池塘,即矩阵中共有多少片相连的 $W$块。
|
||||
|
||||
**输入格式**
|
||||
第一行包含两个整数 $N$ 和 $M$。
|
||||
|
||||
接下来 $N$ 行,每行包含 $M$ 个字符,字符为”$W$”或”$.$”,用以表示矩形土地的积水状况,字符之间没有空格。
|
||||
|
||||
**输出格式**
|
||||
输出一个整数,表示池塘数目。
|
||||
|
||||
**数据范围**
|
||||
```cpp {.line-numbers}
|
||||
1≤N,M≤1000
|
||||
```
|
||||
**输入样例**:
|
||||
```cpp {.line-numbers}
|
||||
10 12
|
||||
W........WW.
|
||||
.WWW.....WWW
|
||||
....WW...WW.
|
||||
.........WW.
|
||||
.........W..
|
||||
..W......W..
|
||||
.W.W.....WW.
|
||||
W.W.W.....W.
|
||||
.W.W......W.
|
||||
..W.......W.
|
||||
```
|
||||
|
||||
**输出样例**:
|
||||
```cpp {.line-numbers}
|
||||
3
|
||||
```
|
||||
|
||||
### 二、总结
|
||||
$Flood \ Fill$问题比较经典的作法有两种: **宽搜** 和 **深搜**,代码写好的话,两种方法没有区别,不存在说数据量大了就深搜不了的问题。
|
||||
|
||||
要严格使用$yxc$老师讲的$hh=0,tt=-1$的模拟队列方式,不要使用$STL$中的$queue$!,原因很简单,有时候,比如斜率优化中,我们可能还要从队头出队列,那样的话还需要使用 **双端队列** ,还如直接来这个模拟更方便!
|
||||
|
||||
### 三、广度优先搜索
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int N = 1010, M = N * N;
|
||||
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
|
||||
// 方向数组
|
||||
int dx[] = {1, 0, -1, 0, 1, -1, 1, -1};
|
||||
int dy[] = {0, 1, 0, -1, 1, -1, -1, 1};
|
||||
|
||||
int n, m;
|
||||
char g[N][N];
|
||||
|
||||
int ans;
|
||||
|
||||
PII q[M];
|
||||
// 宽搜
|
||||
void bfs(int sx, int sy) {
|
||||
int hh = 0, tt = -1;
|
||||
|
||||
q[++tt] = {sx, sy}; // 起点入队列
|
||||
g[sx][sy] = '.'; // 标识为已使用过,防止回头路
|
||||
|
||||
while (hh <= tt) {
|
||||
PII t = q[hh++]; // 一边取队列头,一边出队列
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int tx = t.x + dx[i];
|
||||
int ty = t.y + dy[i];
|
||||
if (g[tx][ty] == 'W') {
|
||||
g[tx][ty] = '.';
|
||||
q[++tt] = {tx, ty};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
// n行m列
|
||||
cin >> n >> m;
|
||||
// 读入地图
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
// 枚举每个位置
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (g[i][j] == 'W') {
|
||||
bfs(i, j); // 开始宽搜
|
||||
ans++; // 找到一块
|
||||
}
|
||||
printf("%d\n", ans);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 四、深度优先搜索
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
int n, m, ans;
|
||||
char g[N][N];
|
||||
|
||||
// 方向数组
|
||||
int dx[] = {1, 0, -1, 0, 1, -1, 1, -1};
|
||||
int dy[] = {0, 1, 0, -1, 1, -1, -1, 1};
|
||||
|
||||
void dfs(int x, int y) {
|
||||
g[x][y] = '.'; // 这样可以不用使用st数组,妙!
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int tx = x + dx[i], ty = y + dy[i];
|
||||
if (g[tx][ty] == 'W') dfs(tx, ty);
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
// 使有深度优先搜索时,注意下标从1开始,这样相当于构建了一个四周为0的墙,不会越界
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (g[i][j] == 'W') { // 找到“ W ”.
|
||||
dfs(i, j);
|
||||
ans++; // 统计答案
|
||||
}
|
||||
printf("%d\n", ans);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
57
TangDou/AcWing/FloodFill/1097_bfs.cpp
Normal file
57
TangDou/AcWing/FloodFill/1097_bfs.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int N = 1010, M = N * N;
|
||||
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
|
||||
// 方向数组
|
||||
int dx[] = {1, 0, -1, 0, 1, -1, 1, -1};
|
||||
int dy[] = {0, 1, 0, -1, 1, -1, -1, 1};
|
||||
|
||||
int n, m;
|
||||
char g[N][N];
|
||||
|
||||
int ans;
|
||||
|
||||
PII q[M];
|
||||
// 宽搜
|
||||
void bfs(int sx, int sy) {
|
||||
int hh = 0, tt = -1;
|
||||
|
||||
q[++tt] = {sx, sy}; // 起点入队列
|
||||
g[sx][sy] = '.'; // 标识为已使用过,防止回头路
|
||||
|
||||
while (hh <= tt) {
|
||||
PII t = q[hh++]; // 一边取队列头,一边出队列
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int tx = t.x + dx[i];
|
||||
int ty = t.y + dy[i];
|
||||
if (g[tx][ty] == 'W') {
|
||||
g[tx][ty] = '.';
|
||||
q[++tt] = {tx, ty};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
// n行m列
|
||||
cin >> n >> m;
|
||||
// 读入地图
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
// 枚举每个位置
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (g[i][j] == 'W') {
|
||||
bfs(i, j); // 开始宽搜
|
||||
ans++; // 找到一块
|
||||
}
|
||||
printf("%d\n", ans);
|
||||
return 0;
|
||||
}
|
||||
34
TangDou/AcWing/FloodFill/1097_dfs.cpp
Normal file
34
TangDou/AcWing/FloodFill/1097_dfs.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
int n, m, ans;
|
||||
char g[N][N];
|
||||
|
||||
// 方向数组
|
||||
int dx[] = {1, 0, -1, 0, 1, -1, 1, -1};
|
||||
int dy[] = {0, 1, 0, -1, 1, -1, -1, 1};
|
||||
|
||||
void dfs(int x, int y) {
|
||||
g[x][y] = '.'; // 这样可以不用使用st数组,妙!
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int tx = x + dx[i], ty = y + dy[i];
|
||||
if (g[tx][ty] == 'W') dfs(tx, ty);
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
// 使有深度优先搜索时,注意下标从1开始,这样相当于构建了一个四周为0的墙,不会越界
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (g[i][j] == 'W') { // 找到“ W ”.
|
||||
dfs(i, j);
|
||||
ans++; // 统计答案
|
||||
}
|
||||
printf("%d\n", ans);
|
||||
return 0;
|
||||
}
|
||||
333
TangDou/AcWing/FloodFill/1098.md
Normal file
333
TangDou/AcWing/FloodFill/1098.md
Normal file
@@ -0,0 +1,333 @@
|
||||
## [$AcWing$ $1098$. 城堡问题](https://www.acwing.com/problem/content/1100/)
|
||||
|
||||
### 一、题目描述
|
||||
```cpp {.line-numbers}
|
||||
1 2 3 4 5 6 7
|
||||
#############################
|
||||
1 # | # | # | | #
|
||||
#####---#####---#---#####---#
|
||||
2 # # | # # # # #
|
||||
#---#####---#####---#####---#
|
||||
3 # | | # # # # #
|
||||
#---#########---#####---#---#
|
||||
4 # # | | | | # #
|
||||
#############################
|
||||
(图 1)
|
||||
|
||||
# = Wall
|
||||
| = No wall
|
||||
- = No wall
|
||||
|
||||
方向:上北下南左西右东。
|
||||
```
|
||||
图$1$是一个城堡的地形图。
|
||||
|
||||
请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。
|
||||
|
||||
城堡被分割成 $m∗n$ 个方格区域,每个方格区域可以有$0\sim 4$面墙。
|
||||
|
||||
注意:墙体厚度忽略不计。
|
||||
|
||||
**输入格式**
|
||||
第一行包含两个整数 $m$ 和 $n$,分别表示城堡南北方向的长度和东西方向的长度。
|
||||
|
||||
接下来 $m$ 行,每行包含 $n$ 个整数,每个整数都表示平面图对应位置的方块的墙的特征。
|
||||
|
||||
每个方块中墙的特征由数字 $P$ 来描述,我们用$1$表示西墙,$2$表示北墙,$4$表示东墙,$8$表示南墙,$P$ 为该方块包含墙的数字之和。
|
||||
|
||||
例如,如果一个方块的 $P$ 为$3$,则 $3 = 1 + 2$,该方块包含西墙和北墙。
|
||||
|
||||
城堡的内墙被计算两次,方块$(1,1)$的南墙同时也是方块$(2,1)$的北墙。
|
||||
|
||||
输入的数据保证城堡至少有两个房间。
|
||||
|
||||
**输出格式**
|
||||
共两行,第一行输出房间总数,第二行输出最大房间的面积(方块数)。
|
||||
|
||||
**数据范围**
|
||||
$1≤m,n≤50,0≤P≤15$
|
||||
|
||||
**输入样例**:
|
||||
```cpp {.line-numbers}
|
||||
4 7
|
||||
11 6 11 6 3 10 6
|
||||
7 9 6 13 5 15 5
|
||||
1 10 12 7 13 7 5
|
||||
13 11 10 8 10 12 13
|
||||
```
|
||||
|
||||
**输出样例**:
|
||||
```cpp {.line-numbers}
|
||||
5
|
||||
9
|
||||
```
|
||||
|
||||
### 二、前置知识
|
||||
这道题的输入挺有意思,为一些数字,其实是理解为$0 \sim 15$的十进制数,转为二进制就是$(0000)_2 \sim (1111)_2$,这四个数位,是表示四个方向:
|
||||
|
||||
**$1$表示西墙,$2$表示北墙,$4$表示东墙,$8$表示南墙**
|
||||
|
||||
每个方向存在数字$1$表示此方向 **有墙**,$0$表示 **无墙**
|
||||
|
||||
用墙维起来的是房间,求 **房间个数** 和 **最大面积**
|
||||
|
||||
|
||||
对于十进制转二进制,并输出二进制的每个数位的值,办法如下:
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
//将十进制转为二进制,并输出每个二进制位的值是0还是1
|
||||
int n = 13;
|
||||
for (int i = 3; i >= 0; i--)//共4个数位
|
||||
cout << (n >> i & 1) << " ";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 三、$bfs$宽度优先搜索
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
const int N = 55, M = N * N;
|
||||
|
||||
int n, m;
|
||||
int g[N][N]; // 地图
|
||||
PII q[M]; // 队列
|
||||
bool st[N][N]; // 标识是否走过
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; // 左上右下
|
||||
int dy[] = {-1, 0, 1, 0}; // 西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
int bfs(int sx, int sy) {
|
||||
int hh = 0, tt = -1;
|
||||
q[++tt] = {sx, sy};
|
||||
st[sx][sy] = true;
|
||||
|
||||
// 一次bfs跑一个连通块,统计一个连通块的面积
|
||||
int area = 1; // 既然能入队列,最起码有入口房间,是一个面积
|
||||
|
||||
while (hh <= tt) {
|
||||
PII t = q[hh++];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int tx = t.x + dx[i], ty = t.y + dy[i];
|
||||
if (tx == 0 || tx > n || ty == 0 || ty > m) continue;
|
||||
if (st[tx][ty]) continue;
|
||||
// 1表示西墙,2表示北墙,4表示东墙,8表示南墙
|
||||
if (g[t.x][t.y] >> i & 1) continue; // 前进的方向上有墙
|
||||
// 连通的房间入队列
|
||||
q[++tt] = {tx, ty};
|
||||
st[tx][ty] = true;
|
||||
area++; // 入队列时房间面积++
|
||||
}
|
||||
}
|
||||
return area;
|
||||
}
|
||||
int cnt, area; // 房间数,最大面积
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
// 从此点出发找连通块
|
||||
area = max(area, bfs(i, j));
|
||||
cnt++; // 记录连通块个数
|
||||
}
|
||||
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### 四、$dfs$深度优先搜索 [$void$+全局变量法]
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 55;
|
||||
int g[N][N];
|
||||
int st[N][N];
|
||||
int n, m, ans; //注意这里的ans不能做为dfs参数进行传递,因为维护的是同一个变量
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; //左上右上
|
||||
int dy[] = {-1, 0, 1, 0}; //西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
void dfs(int sx, int sy) {
|
||||
st[sx][sy] = true; //标识此位置已访问过
|
||||
ans++; //到达一个位置,那么面积肯定增大一个
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = sx + dx[i], y = sy + dy[i];
|
||||
if (x == 0 || x > n || y == 0 || y > m) continue;
|
||||
if (st[x][y]) continue;
|
||||
if (g[sx][sy] >> i & 1) continue; //自带数位压缩表示法~,有墙
|
||||
dfs(x, y);
|
||||
}
|
||||
}
|
||||
int cnt, area;
|
||||
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
cnt++; //连通块数量
|
||||
ans = 0; //清零重新统计
|
||||
dfs(i, j); //开始Flood Fill
|
||||
area = max(area, ans); // PK目前的最大面积
|
||||
}
|
||||
//输出结果
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 五、$dfs$深度优先搜索 [$int$+参数变量法]
|
||||
```c++
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 55;
|
||||
int g[N][N];
|
||||
int st[N][N];
|
||||
int n, m;
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; // 左上右上
|
||||
int dy[] = {-1, 0, 1, 0}; // 西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
int dfs(int sx, int sy) {
|
||||
st[sx][sy] = true; // 标识此位置已访问过
|
||||
int ans = 1; // 自己贡献一个面积
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int tx = sx + dx[i], ty = sy + dy[i];
|
||||
if (tx == 0 || tx > n || ty == 0 || ty > m) continue;
|
||||
if (st[tx][ty]) continue;
|
||||
if (g[sx][sy] >> i & 1) continue; // 自带数位压缩表示法~,有墙
|
||||
ans += dfs(tx, ty); // 孩子们继续贡献面积
|
||||
}
|
||||
return ans; // 我们的总面积
|
||||
}
|
||||
int cnt, area;
|
||||
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
cnt++; // 连通块数量
|
||||
area = max(area, dfs(i, j)); // PK目前的最大面积
|
||||
}
|
||||
// 输出结果
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 六、总结与反思
|
||||
* $bfs$时,需要注意一下起点的初始加入,标记个数++
|
||||
* $dfs$时,也是在进入函数执行时,就意味着此位置可达,标识个数++
|
||||
|
||||
### 七、并查集解法
|
||||
> $2022-10-08$晚 补充
|
||||
|
||||
这道题难度并不大,只要熟练使用 **并查集** 即可。给我们一张地图,`#`代表墙壁,我们用`1`表示西墙,`2`表示北墙,`4`表示东墙,`8`表示南墙,将**没有墙壁隔开的连通的方块记为一个房间**,题目共有两问:
|
||||
|
||||
* 房间的总数
|
||||
* 最大的房间的面积
|
||||
|
||||
这道题可以使用 **并查集** 和 $FloodFill$ 来做,这里讲解一下使用 **并查集** 的做法。
|
||||
|
||||
我们可以从城堡 **左上角** 开始 **逐行枚举**,每次枚举 **北边** 和 **东边** 两个方向(当然,也可以是 **南边** 和 **东边** 两个方向),就可以**不漏的将可以连接在一起的房间都尽可能的连通**,假设枚举到的位置的数$p=9$,我们需要枚举他东边的墙,我们只需要让$p \& 4$判断是否是$1$,若是$1$则证明 **东边有墙** 。当枚举到的方向没有墙的话,我们就是用 **并查集** 将这两个方块加入到一个连通块中,初始`cnt=n*m`,也就是开始的时候有`n*m`个房间,我们 **连通一次,少一个房间**,最后`cnt`就是房间的个数。
|
||||
|
||||
如图,共有五个房间:
|
||||
<center><img src='https://www.freesion.com/images/447/a50c75a6140b30314b43507557b0d6b7.png'></center>
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int N = 55, M = N * N;
|
||||
|
||||
// 1表示西墙,2表示北墙,4表示东墙,8表示南墙
|
||||
|
||||
// 模拟向北和向东两个方向
|
||||
int dx[] = {-1, 0}; // 向北走时,x-1,y不变;向东走时,x不变,y+1
|
||||
int dy[] = {0, 1};
|
||||
int dw[] = {2, 4}; // 2:北墙, 4:东墙
|
||||
|
||||
// 模拟向东和向南两个方向 也是可以的
|
||||
// int dx[] = {0, 1}; //向东走时,x不变,y+1; 向南走时,x+1,y不变
|
||||
// int dy[] = {1, 0};
|
||||
// int dw[] = {4, 8}; // 4:东墙, 8:南墙
|
||||
|
||||
// 模拟向南向西 也是可以的
|
||||
// int dx[] = {1, 0}; //向南走时,x+1,y不变;向西走时,x不变,y-1
|
||||
// int dy[] = {0, -1};
|
||||
// int dw[] = {8, 1}; // 8:南墙, 1:西墙
|
||||
|
||||
// 模拟向南向东 也是可以的
|
||||
// int dx[] = {1, 0}; //向南走时,x+1,y不变;向东走时,x不变,y+1
|
||||
// int dy[] = {0, 1};
|
||||
// int dw[] = {8, 4}; // 8:南墙, 4:东墙
|
||||
|
||||
int n, m; // n行m列
|
||||
int g[N][N]; // 地图
|
||||
int p[M], sz[M]; // 并查集数组,与并查集内成员个数
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 读入地图
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
// 初始化并查集
|
||||
for (int i = 0; i < M; i++) p[i] = i, sz[i] = 1; // 每个人是自己的祖先,并且自己家族的成员数量为1
|
||||
|
||||
int cnt = n * m, area = 1; // 总共最多有n*m个房间,最大面积最小是1
|
||||
|
||||
// 开始从上到下,从左到右,枚举每个位置
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
// 探讨这个位置的房间,是不是能够向北和向东两个方向前进,也就是检查向北和向东是不是会遇到墙
|
||||
for (int u = 0; u < 2; u++)
|
||||
if (!(g[i][j] & dw[u])) { // 如果没有遇到墙
|
||||
int x = i + dx[u], y = j + dy[u]; // 获取新的位置
|
||||
if (x <= 0 || x > n || y <= 0 || y > m) continue; // 出界判断
|
||||
int a = (i - 1) * m + j, b = (x - 1) * m + y; // a是指原位置的编号,b是指拓展后位置的编号
|
||||
// int a = i * m + j, b = x * m + y; //并查集的编号其实是很灵活的,不是非得啥号不可.上面的那行代码一样可以AC
|
||||
|
||||
// join 合并并查集
|
||||
a = find(a), b = find(b);
|
||||
if (a != b) {
|
||||
cnt--; // 合并后,连通块的数量将少了一个
|
||||
sz[b] += sz[a]; // a家族成员加入b家族中
|
||||
p[a] = b; // a认b做父亲
|
||||
area = max(area, sz[b]); // 更新答案
|
||||
}
|
||||
}
|
||||
// 输出房间总数,输出最大面积
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
11
TangDou/AcWing/FloodFill/1098_0.cpp
Normal file
11
TangDou/AcWing/FloodFill/1098_0.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
//将十进制转为二进制,并输出每个二进制位的值是0还是1
|
||||
int n = 13;
|
||||
for (int i = 3; i >= 0; i--)
|
||||
cout << (n >> i & 1) << " ";
|
||||
return 0;
|
||||
}
|
||||
72
TangDou/AcWing/FloodFill/1098_bcj.cpp
Normal file
72
TangDou/AcWing/FloodFill/1098_bcj.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int N = 55, M = N * N;
|
||||
|
||||
// 1表示西墙,2表示北墙,4表示东墙,8表示南墙
|
||||
|
||||
// 模拟向北和向东两个方向
|
||||
int dx[] = {-1, 0}; // 向北走时,x-1,y不变;向东走时,x不变,y+1
|
||||
int dy[] = {0, 1};
|
||||
int dw[] = {2, 4}; // 2:北墙, 4:东墙
|
||||
|
||||
// 模拟向东和向南两个方向 也是可以的
|
||||
// int dx[] = {0, 1}; //向东走时,x不变,y+1; 向南走时,x+1,y不变
|
||||
// int dy[] = {1, 0};
|
||||
// int dw[] = {4, 8}; // 4:东墙, 8:南墙
|
||||
|
||||
// 模拟向南向西 也是可以的
|
||||
// int dx[] = {1, 0}; //向南走时,x+1,y不变;向西走时,x不变,y-1
|
||||
// int dy[] = {0, -1};
|
||||
// int dw[] = {8, 1}; // 8:南墙, 1:西墙
|
||||
|
||||
// 模拟向南向东 也是可以的
|
||||
// int dx[] = {1, 0}; //向南走时,x+1,y不变;向东走时,x不变,y+1
|
||||
// int dy[] = {0, 1};
|
||||
// int dw[] = {8, 4}; // 8:南墙, 4:东墙
|
||||
|
||||
int n, m; // n行m列
|
||||
int g[N][N]; // 地图
|
||||
int p[M], sz[M]; // 并查集数组,与并查集内成员个数
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 读入地图
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
// 初始化并查集
|
||||
for (int i = 0; i < M; i++) p[i] = i, sz[i] = 1; // 每个人是自己的祖先,并且自己家族的成员数量为1
|
||||
|
||||
int cnt = n * m, area = 1; // 总共最多有n*m个房间,最大面积最小是1
|
||||
|
||||
// 开始从上到下,从左到右,枚举每个位置
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
// 探讨这个位置的房间,是不是能够向北和向东两个方向前进,也就是检查向北和向东是不是会遇到墙
|
||||
for (int u = 0; u < 2; u++)
|
||||
if (!(g[i][j] & dw[u])) { // 如果没有遇到墙
|
||||
int x = i + dx[u], y = j + dy[u]; // 获取新的位置
|
||||
if (x <= 0 || x > n || y <= 0 || y > m) continue; // 出界判断
|
||||
int a = (i - 1) * m + j, b = (x - 1) * m + y; // a是指原位置的编号,b是指拓展后位置的编号
|
||||
// int a = i * m + j, b = x * m + y; //并查集的编号其实是很灵活的,不是非得啥号不可.上面的那行代码一样可以AC
|
||||
|
||||
// join 合并并查集
|
||||
a = find(a), b = find(b);
|
||||
if (a != b) {
|
||||
cnt--; // 合并后,连通块的数量将少了一个
|
||||
sz[b] += sz[a]; // a家族成员加入b家族中
|
||||
p[a] = b; // a认b做父亲
|
||||
area = max(area, sz[b]); // 更新答案
|
||||
}
|
||||
}
|
||||
// 输出房间总数,输出最大面积
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
58
TangDou/AcWing/FloodFill/1098_bfs.cpp
Normal file
58
TangDou/AcWing/FloodFill/1098_bfs.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
const int N = 55, M = N * N;
|
||||
|
||||
int n, m;
|
||||
int g[N][N]; // 地图
|
||||
PII q[M]; // 队列
|
||||
bool st[N][N]; // 标识是否走过
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; // 左上右下
|
||||
int dy[] = {-1, 0, 1, 0}; // 西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
int bfs(int sx, int sy) {
|
||||
int hh = 0, tt = -1;
|
||||
q[++tt] = {sx, sy};
|
||||
st[sx][sy] = true;
|
||||
|
||||
// 一次bfs跑一个连通块,统计一个连通块的面积
|
||||
int area = 1; // 既然能入队列,最起码有入口房间,是一个面积
|
||||
|
||||
while (hh <= tt) {
|
||||
PII t = q[hh++];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int tx = t.x + dx[i], ty = t.y + dy[i];
|
||||
if (tx == 0 || tx > n || ty == 0 || ty > m) continue;
|
||||
if (st[tx][ty]) continue;
|
||||
// 1表示西墙,2表示北墙,4表示东墙,8表示南墙
|
||||
if (g[t.x][t.y] >> i & 1) continue; // 前进的方向上有墙
|
||||
// 连通的房间入队列
|
||||
q[++tt] = {tx, ty};
|
||||
st[tx][ty] = true;
|
||||
area++; // 入队列时房间面积++
|
||||
}
|
||||
}
|
||||
return area;
|
||||
}
|
||||
int cnt, area; // 房间数,最大面积
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
// 从此点出发找连通块
|
||||
area = max(area, bfs(i, j));
|
||||
cnt++; // 记录连通块个数
|
||||
}
|
||||
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
42
TangDou/AcWing/FloodFill/1098_dfs_1.cpp
Normal file
42
TangDou/AcWing/FloodFill/1098_dfs_1.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 55;
|
||||
int g[N][N];
|
||||
int st[N][N];
|
||||
int n, m, ans; //注意这里的ans不能做为dfs参数进行传递,因为维护的是同一个变量
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; //左上右上
|
||||
int dy[] = {-1, 0, 1, 0}; //西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
void dfs(int sx, int sy) {
|
||||
st[sx][sy] = true; //标识此位置已访问过
|
||||
ans++; //到达一个位置,那么面积肯定增大一个
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = sx + dx[i], y = sy + dy[i];
|
||||
if (x == 0 || x > n || y == 0 || y > m) continue;
|
||||
if (st[x][y]) continue;
|
||||
if (g[sx][sy] >> i & 1) continue; //自带数位压缩表示法~,有墙
|
||||
dfs(x, y);
|
||||
}
|
||||
}
|
||||
int cnt, area;
|
||||
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
cnt++; //连通块数量
|
||||
ans = 0; //清零重新统计
|
||||
dfs(i, j); //开始Flood Fill
|
||||
area = max(area, ans); // PK目前的最大面积
|
||||
}
|
||||
//输出结果
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
40
TangDou/AcWing/FloodFill/1098_dfs_2.cpp
Normal file
40
TangDou/AcWing/FloodFill/1098_dfs_2.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 55;
|
||||
int g[N][N];
|
||||
int st[N][N];
|
||||
int n, m;
|
||||
|
||||
int dx[] = {0, -1, 0, 1}; // 左上右上
|
||||
int dy[] = {-1, 0, 1, 0}; // 西北东南 1 2 4 8 二进制位运算,参考1098_0.cpp
|
||||
|
||||
int dfs(int sx, int sy) {
|
||||
st[sx][sy] = true; // 标识此位置已访问过
|
||||
int ans = 1; // 自己贡献一个面积
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int tx = sx + dx[i], ty = sy + dy[i];
|
||||
if (tx == 0 || tx > n || ty == 0 || ty > m) continue;
|
||||
if (st[tx][ty]) continue;
|
||||
if (g[sx][sy] >> i & 1) continue; // 自带数位压缩表示法~,有墙
|
||||
ans += dfs(tx, ty); // 孩子们继续贡献面积
|
||||
}
|
||||
return ans; // 我们的总面积
|
||||
}
|
||||
int cnt, area;
|
||||
|
||||
int main() {
|
||||
cin >> n >> m;
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
cin >> g[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= m; j++)
|
||||
if (!st[i][j]) {
|
||||
cnt++; // 连通块数量
|
||||
area = max(area, dfs(i, j)); // PK目前的最大面积
|
||||
}
|
||||
// 输出结果
|
||||
printf("%d\n%d\n", cnt, area);
|
||||
return 0;
|
||||
}
|
||||
447
TangDou/AcWing/FloodFill/1106.md
Normal file
447
TangDou/AcWing/FloodFill/1106.md
Normal file
@@ -0,0 +1,447 @@
|
||||
## [$AcWing$ $1106$. 山峰和山谷](https://www.acwing.com/problem/content/1108/)
|
||||
|
||||
### 一、题目描述
|
||||
$FGD$小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。
|
||||
|
||||
为了能够对旅程有一个安排,他想知道山峰和山谷的数量。
|
||||
|
||||
给定一个地图,为$FGD$想要旅行的区域,地图被分为 $n×n$ 的网格,每个格子 $(i,j)$ 的高度 $w(i,j)$ 是给定的。
|
||||
|
||||
若两个格子有公共顶点,那么它们就是相邻的格子,如与 $(i,j)$ 相邻的格子有
|
||||
$(i−1,j−1),(i−1,j),(i−1,j+1),(i,j−1),(i,j+1),(i+1,j−1),(i+1,j),(i+1,j+1)$。
|
||||
|
||||
我们定义一个格子的集合 $S$ 为山峰(山谷)当且仅当:
|
||||
|
||||
1. $S$ 的所有格子都有相同的高度。
|
||||
2. $S$ 的所有格子都连通。
|
||||
3. 对于 $s$ 属于 $S$,与 $s$ 相邻的 $s′$ 不属于 $S$,都有 $w_s>w_{s′}$(山峰),或者 $w_s<w_{s′}$(山谷)。
|
||||
4. 如果周围不存在相邻区域,则同时将其视为山峰和山谷。
|
||||
|
||||
你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。
|
||||
|
||||
### 二、题意理解
|
||||
输入样例1:
|
||||
```c++
|
||||
5
|
||||
8 8 8 7 7
|
||||
7 7 8 8 7
|
||||
7 7 7 7 7
|
||||
7 8 8 7 8
|
||||
7 8 8 8 8
|
||||
```
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2019/10/16/19_0250799aef-1.png'></center>
|
||||
|
||||
|
||||
|
||||
输入样例2:
|
||||
```c++
|
||||
5
|
||||
5 7 8 3 1
|
||||
5 5 7 6 6
|
||||
6 6 6 2 8
|
||||
5 7 2 5 8
|
||||
7 1 0 1 7
|
||||
```
|
||||
<center><img src='https://cdn.acwing.com/media/article/image/2019/10/16/19_08db5e60ef-2.png'></center>
|
||||
|
||||
解释一下这个用例:
|
||||
数字$5$,需要把周围和自己一样的数字连接在一起$(Flood~Fill)$,然后看看周围是不是存在比自己 **高** 的,是不是存在比自己 **矮** 的。
|
||||
* 如果周围没有比自己高的,自己就是山峰
|
||||
* 如果周围没有比自己矮的,自己就是山谷
|
||||
|
||||
### 三、预备知识
|
||||
* 周围八个位置遍历
|
||||
八个位置一般不采用四个位置的方法,即$dx[4]+dy[4]$的形式,而是采用简单粗暴的九宫格遍历二层循环的办法。
|
||||
|
||||
* 函数的多返回值
|
||||
$C++$的多返回值,一般采用传递$\&$地址符参数的方法,让函数内修改的结果返回到调用者手中。
|
||||
|
||||
### 四、$bfs$实现代码
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
const int N = 1010, M = N * N;
|
||||
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
|
||||
int n;
|
||||
int h[N][N];
|
||||
PII q[M];
|
||||
bool st[N][N];
|
||||
|
||||
/*
|
||||
sx,sy:出发的位置
|
||||
has_higher,has_lower:是不是周围发现了比自己高的,比自己矮的
|
||||
*/
|
||||
void bfs(int sx, int sy, bool &has_higher, bool &has_lower) {
|
||||
// 声明队列
|
||||
int hh = 0, tt = -1;
|
||||
// 添加出发点
|
||||
q[++tt] = {sx, sy};
|
||||
st[sx][sy] = true;
|
||||
|
||||
while (hh <= tt) {
|
||||
auto t = q[hh++];
|
||||
// 利用双重循环遍历周围8连通块
|
||||
for (int i = t.x - 1; i <= t.x + 1; i++)
|
||||
for (int j = t.y - 1; j <= t.y + 1; j++) {
|
||||
if (i == 0 || i > n || j == 0 || j > n) continue; // 出地图不行
|
||||
// 下一个目标地点的高度与自己不同,需要进行标识
|
||||
if (h[i][j] != h[t.x][t.y]) {
|
||||
if (h[i][j] > h[t.x][t.y])
|
||||
has_higher = true;
|
||||
else
|
||||
has_lower = true;
|
||||
} else if (!st[i][j]) { // 与自己相同,并且没有走过
|
||||
q[++tt] = {i, j}; // 入队列
|
||||
st[i][j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> n;
|
||||
// 地图
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
// 山峰个数,山谷个数
|
||||
int peak = 0, valley = 0;
|
||||
|
||||
// Flood Fill
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!st[i][j]) { // 发现新的连通块
|
||||
bool has_higher = false, has_lower = false;
|
||||
// bfs遍历连通块,标识并且找出这一块是否存在比它高的,比它矮的,使用引用返回多个值
|
||||
bfs(i, j, has_higher, has_lower);
|
||||
if (!has_higher) peak++; // 没有比自己高的,山峰
|
||||
if (!has_lower) valley++; // 没有比自己矮的,山谷
|
||||
// 由于三种情况,山峰,山谷,即不是山峰也不是山谷,所以不能用else
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 五、$dfs$+引用参数
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
|
||||
// 通过了 20/22个数据
|
||||
int n;
|
||||
bool st[N][N];
|
||||
int h[N][N];
|
||||
/*
|
||||
# 不同操作系统默认栈的大小
|
||||
Linux默认栈空间的大小为8MB,通过命令ulimit -s来设置
|
||||
在Windows下,栈的大小是2MB
|
||||
|
||||
# 原因分析
|
||||
每进行一次递归,都会在栈上多加一层,所以递归太深的话会出现数据溢出的错误。函数调用层次过深,每调用一次,函数的参数、
|
||||
局部变量等信息就压一次栈。
|
||||
|
||||
n=1000 n*n=1e3*1e3=1e6
|
||||
sx,sy 每个int 4byte,所以共8 byte
|
||||
bool has_higher,has_lower 各占1个byte ,所以共2byte
|
||||
1e6*10=1e7 byte = 1e7/1024 kb=9,765.625 kb = 9.53mb
|
||||
|
||||
如果不加上 has_higher,has_lower,就是
|
||||
1e6*8= 8e6/1024 kb=7,812.5kb = 7.6mb
|
||||
|
||||
因AcWing的评测机是GCC搭建在Linux环境中,所以栈的空间默认是8MB(我猜的,不对Y总别骂我~),也就是我们的运气好,采用全局的has_higher,
|
||||
has_lower刚刚好通过这组测试数据,如果再多一点,一样是会挂掉的,这个是递归与栈的本质造成,这时只能采用bfs进行Flood Fill
|
||||
|
||||
# 写给AcWing
|
||||
一般来说,评测时的栈空间限制等于内存限制。但系统默认的栈空间往往较小,有时会出现官方评测时正常运行,而本地测试时爆栈的情况。这时候就需要对栈空间进行更改。
|
||||
现在看来AcWing的栈空间是默认的8MB,而不是CCF官方的栈空间限制等于内存限制,不知道y总是出于什么考虑。
|
||||
参考链接:https://studyingfather.blog.luogu.org/noi-technical-faq
|
||||
|
||||
|
||||
# 解决办法:
|
||||
* 如果递归的层次较多,尽量避免dfs函数的参数个数,防止递归太深导致MLE出现
|
||||
* 避开dfs,采用bfs即可解决,此时内存是在堆上分配的,可以使用3GB或以上
|
||||
|
||||
*/
|
||||
void dfs(int sx, int sy, bool &has_higher, bool &has_lower) {
|
||||
st[sx][sy] = true;
|
||||
for (int x = sx - 1; x <= sx + 1; x++) {
|
||||
for (int y = sy - 1; y <= sy + 1; y++) {
|
||||
if (x <= 0 || x > n || y <= 0 || y > n) continue;
|
||||
if (h[sx][sy] != h[x][y]) { // 高度不相等
|
||||
if (h[sx][sy] < h[x][y]) has_higher = true;
|
||||
if (h[sx][sy] > h[x][y]) has_lower = true;
|
||||
} else { // 高度相等
|
||||
if (st[x][y]) continue;
|
||||
st[x][y] = true;
|
||||
dfs(x, y, has_higher, has_lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int vally, peak;
|
||||
int main() {
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!st[i][j]) {
|
||||
bool has_higher = false, has_lower = false;
|
||||
dfs(i, j, has_higher, has_lower);
|
||||
if (has_higher && has_lower) continue;
|
||||
if (has_higher) vally++;
|
||||
if (has_lower) peak++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于不存在山峰+山谷的一马平地,山峰山谷都输出1
|
||||
if (peak == 0 && vally == 0) peak = 1, vally = 1;
|
||||
printf("%d %d\n", peak, vally);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 六、$dfs$+全局变量
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
|
||||
int n;
|
||||
bool f[N][N];
|
||||
int h[N][N];
|
||||
// 将两个需要返回的参数,设置为全局变量,则可以正常通过此题。
|
||||
// 将两个需要返回的参数,设置为带地址符的变量,则MLE
|
||||
bool has_higher, has_lower;
|
||||
// 657 ms
|
||||
void dfs(int sx, int sy) {
|
||||
f[sx][sy] = true;
|
||||
for (int x = sx - 1; x <= sx + 1; x++) {
|
||||
for (int y = sy - 1; y <= sy + 1; y++) {
|
||||
if (x <= 0 || x > n || y <= 0 || y > n) continue;
|
||||
if (h[sx][sy] != h[x][y]) { // 高度不相等
|
||||
if (h[sx][sy] < h[x][y]) has_higher = true;
|
||||
if (h[sx][sy] > h[x][y]) has_lower = true;
|
||||
} else { // 高度相等
|
||||
if (f[x][y]) continue;
|
||||
dfs(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int vally, peak;
|
||||
int main() {
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!f[i][j]) {
|
||||
has_higher = has_lower = false;
|
||||
dfs(i, j);
|
||||
if (has_higher && has_lower) continue;
|
||||
if (has_higher) vally++;
|
||||
if (has_lower) peak++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于不存在山峰+山谷的一马平地,山峰山谷都输出1
|
||||
if (peak == 0 && vally == 0) peak = 1, vally = 1;
|
||||
printf("%d %d\n", peak, vally);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 七、并查集解法
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
const int M = N * N;
|
||||
|
||||
int n;
|
||||
|
||||
int h[N][N];
|
||||
int st[M][2]; // 第一维:并查集编号,第二维:0:附近的最小值,1:附近的最大值
|
||||
|
||||
// 1692 ms
|
||||
// 8个方向
|
||||
int dx[] = {0, 0, -1, 1, -1, 1, -1, 1}; // 上下左右
|
||||
int dy[] = {1, -1, 0, 0, 1, 1, -1, -1}; // 左下,右下,左上,右上
|
||||
|
||||
int p[M];
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
// 根据坐标获取并查集的编号
|
||||
void getXy(int num, int &x, int &y) {
|
||||
x = (num - 1) / n + 1;
|
||||
y = (num - 1) % n + 1;
|
||||
}
|
||||
// 根据并查集的编号获取坐标
|
||||
int getNum(int x, int y) {
|
||||
return (x - 1) * n + y;
|
||||
}
|
||||
|
||||
int valley, peak;
|
||||
int main() {
|
||||
// 初始化并查集
|
||||
// i为每个格子在并查集中的编号,编号策略为 (i-1)*n+j
|
||||
for (int i = 0; i < M; i++) p[i] = i; // 每个人都是自己的祖先
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
cin >> h[i][j];
|
||||
int num = getNum(i, j);
|
||||
st[num][0] = st[num][1] = h[i][j]; // 初始化
|
||||
}
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++)
|
||||
for (int k = 0; k < 8; k++) {
|
||||
int x = i + dx[k], y = j + dy[k];
|
||||
if (x == 0 || y == 0 || x > n || y > n) continue;
|
||||
// 编号
|
||||
int a = getNum(i, j);
|
||||
int b = getNum(x, y);
|
||||
// 族长
|
||||
int pa = find(a), pb = find(b);
|
||||
|
||||
// 记录我们家族周围最小的
|
||||
if (h[i][j] > h[x][y]) st[pa][0] = min(st[pa][0], h[x][y]);
|
||||
// 记录我们家族周围最大的
|
||||
if (h[i][j] < h[x][y]) st[pa][1] = max(st[pa][1], h[x][y]);
|
||||
|
||||
if (h[i][j] == h[x][y]) {
|
||||
if (pa != pb) { // 合并并查集
|
||||
p[pa] = pb;
|
||||
st[pb][0] = min(st[pb][0], st[pa][0]);
|
||||
st[pb][1] = max(st[pb][1], st[pa][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 没有比自己高的,山峰
|
||||
// 没有比自己矮的,山谷
|
||||
for (int i = 1; i <= n * n; i++) {
|
||||
if (p[i] == i) {
|
||||
int x, y;
|
||||
getXy(i, x, y);
|
||||
if (st[i][0] == h[x][y]) valley++;
|
||||
if (st[i][1] == h[x][y]) peak++;
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 八、并查集优化
|
||||
因为并查集通过双重循环,从左到右,从上到下遍历,所以,可以通过双向记录周边最大最小的办法,让每个不同的区块之间互认,这样就只需要枚举
|
||||
**$1$右** **$2$下** **$3$右下** **$4$左下** 即可。
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
const int M = N * N;
|
||||
|
||||
int n;
|
||||
|
||||
int h[N][N];
|
||||
int st[M][2]; // 第一维:并查集编号,第二维:0:附近的最小值,1:附近的最大值
|
||||
|
||||
// 1132 ms
|
||||
int dx[] = {0, 1, 1, 1}; // 1右 2下 3右下 4左下
|
||||
int dy[] = {1, 0, 1, -1};
|
||||
|
||||
int p[M];
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
// 根据坐标获取并查集的编号
|
||||
void getXy(int num, int &x, int &y) {
|
||||
x = (num - 1) / n + 1;
|
||||
y = (num - 1) % n + 1;
|
||||
}
|
||||
// 根据并查集的编号获取坐标
|
||||
int getNum(int x, int y) {
|
||||
return (x - 1) * n + y;
|
||||
}
|
||||
|
||||
int valley, peak;
|
||||
int main() {
|
||||
cin >> n;
|
||||
// 初始化并查集
|
||||
// i为每个格子在并查集中的编号,编号策略为 (i-1)*n+j
|
||||
for (int i = 0; i < M; i++) p[i] = i; // 每个人都是自己的祖先
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
cin >> h[i][j];
|
||||
int num = getNum(i, j);
|
||||
st[num][0] = st[num][1] = h[i][j]; // 初始化
|
||||
}
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++)
|
||||
for (int k = 0; k < 4; k++) {
|
||||
int x = i + dx[k], y = j + dy[k];
|
||||
if (x == 0 || y == 0 || x > n || y > n) continue;
|
||||
// 编号
|
||||
int a = getNum(i, j), b = getNum(x, y);
|
||||
// 族长
|
||||
int pa = find(a), pb = find(b);
|
||||
|
||||
// 记录我们家族周围最小的
|
||||
if (h[i][j] > h[x][y]) {
|
||||
st[pa][0] = min(st[pa][0], h[x][y]);
|
||||
st[pb][1] = max(st[pb][1], h[i][j]);
|
||||
}
|
||||
// 记录我们家族周围最大的
|
||||
if (h[i][j] < h[x][y]) {
|
||||
st[pa][1] = max(st[pa][1], h[x][y]);
|
||||
st[pb][0] = min(st[pb][0], h[i][j]);
|
||||
}
|
||||
|
||||
if (h[i][j] == h[x][y]) {
|
||||
if (pa != pb) { // 合并并查集
|
||||
p[pa] = pb;
|
||||
st[pb][0] = min(st[pb][0], st[pa][0]);
|
||||
st[pb][1] = max(st[pb][1], st[pa][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 没有比自己高的,山峰
|
||||
// 没有比自己矮的,山谷
|
||||
for (int i = 1; i <= n * n; i++) {
|
||||
if (p[i] == i) {
|
||||
int x, y;
|
||||
getXy(i, x, y);
|
||||
if (st[i][0] == h[x][y]) valley++;
|
||||
if (st[i][1] == h[x][y]) peak++;
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
83
TangDou/AcWing/FloodFill/1106_bcj_1.cpp
Normal file
83
TangDou/AcWing/FloodFill/1106_bcj_1.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
const int M = N * N;
|
||||
|
||||
int n;
|
||||
|
||||
int h[N][N];
|
||||
int st[M][2]; // 第一维:并查集编号,第二维:0:附近的最小值,1:附近的最大值
|
||||
|
||||
// 1692 ms
|
||||
// 8个方向
|
||||
int dx[] = {0, 0, -1, 1, -1, 1, -1, 1}; // 上下左右
|
||||
int dy[] = {1, -1, 0, 0, 1, 1, -1, -1}; // 左下,右下,左上,右上
|
||||
|
||||
int p[M];
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
// 根据坐标获取并查集的编号
|
||||
void getXy(int num, int &x, int &y) {
|
||||
x = (num - 1) / n + 1;
|
||||
y = (num - 1) % n + 1;
|
||||
}
|
||||
// 根据并查集的编号获取坐标
|
||||
int getNum(int x, int y) {
|
||||
return (x - 1) * n + y;
|
||||
}
|
||||
|
||||
int valley, peak;
|
||||
int main() {
|
||||
// 初始化并查集
|
||||
// i为每个格子在并查集中的编号,编号策略为 (i-1)*n+j
|
||||
for (int i = 0; i < M; i++) p[i] = i; // 每个人都是自己的祖先
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
cin >> h[i][j];
|
||||
int num = getNum(i, j);
|
||||
st[num][0] = st[num][1] = h[i][j]; // 初始化
|
||||
}
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++)
|
||||
for (int k = 0; k < 8; k++) {
|
||||
int x = i + dx[k], y = j + dy[k];
|
||||
if (x == 0 || y == 0 || x > n || y > n) continue;
|
||||
// 编号
|
||||
int a = getNum(i, j);
|
||||
int b = getNum(x, y);
|
||||
// 族长
|
||||
int pa = find(a), pb = find(b);
|
||||
|
||||
// 记录我们家族周围最小的
|
||||
if (h[i][j] > h[x][y]) st[pa][0] = min(st[pa][0], h[x][y]);
|
||||
// 记录我们家族周围最大的
|
||||
if (h[i][j] < h[x][y]) st[pa][1] = max(st[pa][1], h[x][y]);
|
||||
|
||||
if (h[i][j] == h[x][y]) {
|
||||
if (pa != pb) { // 合并并查集
|
||||
p[pa] = pb;
|
||||
st[pb][0] = min(st[pb][0], st[pa][0]);
|
||||
st[pb][1] = max(st[pb][1], st[pa][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 没有比自己高的,山峰
|
||||
// 没有比自己矮的,山谷
|
||||
for (int i = 1; i <= n * n; i++) {
|
||||
if (p[i] == i) {
|
||||
int x, y;
|
||||
getXy(i, x, y);
|
||||
if (st[i][0] == h[x][y]) valley++;
|
||||
if (st[i][1] == h[x][y]) peak++;
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
88
TangDou/AcWing/FloodFill/1106_bcj_2.cpp
Normal file
88
TangDou/AcWing/FloodFill/1106_bcj_2.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
const int M = N * N;
|
||||
|
||||
int n;
|
||||
|
||||
int h[N][N];
|
||||
int st[M][2]; // 第一维:并查集编号,第二维:0:附近的最小值,1:附近的最大值
|
||||
|
||||
// 1132 ms
|
||||
int dx[] = {0, 1, 1, 1}; // 1右 2下 3右下 4左下
|
||||
int dy[] = {1, 0, 1, -1};
|
||||
|
||||
int p[M];
|
||||
int find(int x) {
|
||||
if (p[x] != x) p[x] = find(p[x]);
|
||||
return p[x];
|
||||
}
|
||||
|
||||
// 根据坐标获取并查集的编号
|
||||
void getXy(int num, int &x, int &y) {
|
||||
x = (num - 1) / n + 1;
|
||||
y = (num - 1) % n + 1;
|
||||
}
|
||||
// 根据并查集的编号获取坐标
|
||||
int getNum(int x, int y) {
|
||||
return (x - 1) * n + y;
|
||||
}
|
||||
|
||||
int valley, peak;
|
||||
int main() {
|
||||
cin >> n;
|
||||
// 初始化并查集
|
||||
// i为每个格子在并查集中的编号,编号策略为 (i-1)*n+j
|
||||
for (int i = 0; i < M; i++) p[i] = i; // 每个人都是自己的祖先
|
||||
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
cin >> h[i][j];
|
||||
int num = getNum(i, j);
|
||||
st[num][0] = st[num][1] = h[i][j]; // 初始化
|
||||
}
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++)
|
||||
for (int k = 0; k < 4; k++) {
|
||||
int x = i + dx[k], y = j + dy[k];
|
||||
if (x == 0 || y == 0 || x > n || y > n) continue;
|
||||
// 编号
|
||||
int a = getNum(i, j), b = getNum(x, y);
|
||||
// 族长
|
||||
int pa = find(a), pb = find(b);
|
||||
|
||||
// 记录我们家族周围最小的
|
||||
if (h[i][j] > h[x][y]) {
|
||||
st[pa][0] = min(st[pa][0], h[x][y]);
|
||||
st[pb][1] = max(st[pb][1], h[i][j]);
|
||||
}
|
||||
// 记录我们家族周围最大的
|
||||
if (h[i][j] < h[x][y]) {
|
||||
st[pa][1] = max(st[pa][1], h[x][y]);
|
||||
st[pb][0] = min(st[pb][0], h[i][j]);
|
||||
}
|
||||
|
||||
if (h[i][j] == h[x][y]) {
|
||||
if (pa != pb) { // 合并并查集
|
||||
p[pa] = pb;
|
||||
st[pb][0] = min(st[pb][0], st[pa][0]);
|
||||
st[pb][1] = max(st[pb][1], st[pa][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 没有比自己高的,山峰
|
||||
// 没有比自己矮的,山谷
|
||||
for (int i = 1; i <= n * n; i++) {
|
||||
if (p[i] == i) {
|
||||
int x, y;
|
||||
getXy(i, x, y);
|
||||
if (st[i][0] == h[x][y]) valley++;
|
||||
if (st[i][1] == h[x][y]) peak++;
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
70
TangDou/AcWing/FloodFill/1106_bfs.cpp
Normal file
70
TangDou/AcWing/FloodFill/1106_bfs.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
const int N = 1010, M = N * N;
|
||||
|
||||
typedef pair<int, int> PII;
|
||||
#define x first
|
||||
#define y second
|
||||
|
||||
int n;
|
||||
int h[N][N];
|
||||
PII q[M];
|
||||
bool st[N][N];
|
||||
|
||||
/*
|
||||
sx,sy:出发的位置
|
||||
has_higher,has_lower:是不是周围发现了比自己高的,比自己矮的
|
||||
*/
|
||||
void bfs(int sx, int sy, bool &has_higher, bool &has_lower) {
|
||||
// 声明队列
|
||||
int hh = 0, tt = -1;
|
||||
// 添加出发点
|
||||
q[++tt] = {sx, sy};
|
||||
st[sx][sy] = true;
|
||||
|
||||
while (hh <= tt) {
|
||||
auto t = q[hh++];
|
||||
// 利用双重循环遍历周围8连通块
|
||||
for (int i = t.x - 1; i <= t.x + 1; i++)
|
||||
for (int j = t.y - 1; j <= t.y + 1; j++) {
|
||||
if (i == 0 || i > n || j == 0 || j > n) continue; // 出地图不行
|
||||
// 下一个目标地点的高度与自己不同,需要进行标识
|
||||
if (h[i][j] != h[t.x][t.y]) {
|
||||
if (h[i][j] > h[t.x][t.y])
|
||||
has_higher = true;
|
||||
else
|
||||
has_lower = true;
|
||||
} else if (!st[i][j]) { // 与自己相同,并且没有走过
|
||||
q[++tt] = {i, j}; // 入队列
|
||||
st[i][j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> n;
|
||||
// 地图
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
// 山峰个数,山谷个数
|
||||
int peak = 0, valley = 0;
|
||||
|
||||
// Flood Fill
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!st[i][j]) { // 发现新的连通块
|
||||
bool has_higher = false, has_lower = false;
|
||||
// bfs遍历连通块,标识并且找出这一块是否存在比它高的,比它矮的,使用引用返回多个值
|
||||
bfs(i, j, has_higher, has_lower);
|
||||
if (!has_higher) peak++; // 没有比自己高的,山峰
|
||||
if (!has_lower) valley++; // 没有比自己矮的,山谷
|
||||
// 由于三种情况,山峰,山谷,即不是山峰也不是山谷,所以不能用else
|
||||
}
|
||||
}
|
||||
printf("%d %d\n", peak, valley);
|
||||
return 0;
|
||||
}
|
||||
78
TangDou/AcWing/FloodFill/1106_dfs_1.cpp
Normal file
78
TangDou/AcWing/FloodFill/1106_dfs_1.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
|
||||
// 通过了 20/22个数据
|
||||
int n;
|
||||
bool st[N][N];
|
||||
int h[N][N];
|
||||
/*
|
||||
# 不同操作系统默认栈的大小
|
||||
Linux默认栈空间的大小为8MB,通过命令ulimit -s来设置
|
||||
在Windows下,栈的大小是2MB
|
||||
|
||||
# 原因分析
|
||||
每进行一次递归,都会在栈上多加一层,所以递归太深的话会出现数据溢出的错误。函数调用层次过深,每调用一次,函数的参数、
|
||||
局部变量等信息就压一次栈。
|
||||
|
||||
n=1000 n*n=1e3*1e3=1e6
|
||||
sx,sy 每个int 4byte,所以共8 byte
|
||||
bool has_higher,has_lower 各占1个byte ,所以共2byte
|
||||
1e6*10=1e7 byte = 1e7/1024 kb=9,765.625 kb = 9.53mb
|
||||
|
||||
如果不加上 has_higher,has_lower,就是
|
||||
1e6*8= 8e6/1024 kb=7,812.5kb = 7.6mb
|
||||
|
||||
因AcWing的评测机是GCC搭建在Linux环境中,所以栈的空间默认是8MB(我猜的,不对Y总别骂我~),也就是我们的运气好,采用全局的has_higher,
|
||||
has_lower刚刚好通过这组测试数据,如果再多一点,一样是会挂掉的,这个是递归与栈的本质造成,这时只能采用bfs进行Flood Fill
|
||||
|
||||
# 写给AcWing
|
||||
一般来说,评测时的栈空间限制等于内存限制。但系统默认的栈空间往往较小,有时会出现官方评测时正常运行,而本地测试时爆栈的情况。这时候就需要对栈空间进行更改。
|
||||
现在看来AcWing的栈空间是默认的8MB,而不是CCF官方的栈空间限制等于内存限制,不知道y总是出于什么考虑。
|
||||
参考链接:https://studyingfather.blog.luogu.org/noi-technical-faq
|
||||
|
||||
|
||||
# 解决办法:
|
||||
* 如果递归的层次较多,尽量避免dfs函数的参数个数,防止递归太深导致MLE出现
|
||||
* 避开dfs,采用bfs即可解决,此时内存是在堆上分配的,可以使用3GB或以上
|
||||
|
||||
*/
|
||||
void dfs(int sx, int sy, bool &has_higher, bool &has_lower) {
|
||||
st[sx][sy] = true;
|
||||
for (int x = sx - 1; x <= sx + 1; x++) {
|
||||
for (int y = sy - 1; y <= sy + 1; y++) {
|
||||
if (x <= 0 || x > n || y <= 0 || y > n) continue;
|
||||
if (h[sx][sy] != h[x][y]) { // 高度不相等
|
||||
if (h[sx][sy] < h[x][y]) has_higher = true;
|
||||
if (h[sx][sy] > h[x][y]) has_lower = true;
|
||||
} else { // 高度相等
|
||||
if (st[x][y]) continue;
|
||||
st[x][y] = true;
|
||||
dfs(x, y, has_higher, has_lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int vally, peak;
|
||||
int main() {
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!st[i][j]) {
|
||||
bool has_higher = false, has_lower = false;
|
||||
dfs(i, j, has_higher, has_lower);
|
||||
if (has_higher && has_lower) continue;
|
||||
if (has_higher) vally++;
|
||||
if (has_lower) peak++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于不存在山峰+山谷的一马平地,山峰山谷都输出1
|
||||
if (peak == 0 && vally == 0) peak = 1, vally = 1;
|
||||
printf("%d %d\n", peak, vally);
|
||||
return 0;
|
||||
}
|
||||
49
TangDou/AcWing/FloodFill/1106_dfs_2.cpp
Normal file
49
TangDou/AcWing/FloodFill/1106_dfs_2.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 1010;
|
||||
|
||||
int n;
|
||||
bool f[N][N];
|
||||
int h[N][N];
|
||||
// 将两个需要返回的参数,设置为全局变量,则可以正常通过此题。
|
||||
// 将两个需要返回的参数,设置为带地址符的变量,则MLE
|
||||
bool has_higher, has_lower;
|
||||
// 657 ms
|
||||
void dfs(int sx, int sy) {
|
||||
f[sx][sy] = true;
|
||||
for (int x = sx - 1; x <= sx + 1; x++) {
|
||||
for (int y = sy - 1; y <= sy + 1; y++) {
|
||||
if (x <= 0 || x > n || y <= 0 || y > n) continue;
|
||||
if (h[sx][sy] != h[x][y]) { // 高度不相等
|
||||
if (h[sx][sy] < h[x][y]) has_higher = true;
|
||||
if (h[sx][sy] > h[x][y]) has_lower = true;
|
||||
} else { // 高度相等
|
||||
if (f[x][y]) continue;
|
||||
dfs(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int vally, peak;
|
||||
int main() {
|
||||
for (int i = 1; i <= n; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
cin >> h[i][j];
|
||||
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (!f[i][j]) {
|
||||
has_higher = has_lower = false;
|
||||
dfs(i, j);
|
||||
if (has_higher && has_lower) continue;
|
||||
if (has_higher) vally++;
|
||||
if (has_lower) peak++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于不存在山峰+山谷的一马平地,山峰山谷都输出1
|
||||
if (peak == 0 && vally == 0) peak = 1, vally = 1;
|
||||
printf("%d %d\n", peak, vally);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user