P1596 [USACO10OCT] Lake Counting S
我的洛谷主页持续更新题解,预览效果更好
题目
题目描述
Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100) squares. Each square contains either water ('W') or dry land ('.'). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors. Given a diagram of Farmer John's field, determine how many ponds he has.
输入格式
Line 1: Two space-separated integers: N and M * Lines 2..N+1: M characters per line representing one row of Farmer John's field. Each character is either 'W' or '.'. The characters do not have spaces between them.
输出格式
Line 1: The number of ponds in Farmer John's field.
题意翻译
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 的网格图表示。每个网格中有水(`W`) 或是旱地(`.`)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。
深度优先搜索 - 洪水填充
这是一道超级无敌经典的洪水填充题目。
我们需要找到这位经常求助于我们的农场主约翰先生的田地里的水谭数量。
这里,我们可以采用深度优先搜索(Deep First Search, )。这种题目还有一种叫法——洪水填充(Flood Fill)。
首先,我们需要`2`个数组,用来存放像周围八个点的坐标位置的加减关系。(把这个田地抽象成一个坐标系,行x,列y)。这些数值是以行列坐标填写的。
int dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
int dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
然后就是深搜。
void dfs(int x, int y) {
}
定义一个函数`dfs`,有`2`个参数`x, y`,代表着行坐标和列坐标。
if (x < 1 || x > n || y < 1 || y > m || a[x][y] != 'W')
return ;
接着,进行判断。
如果`x`不在正确的坐标(`x < 1 || x > n`)
或者`y`不在正确的坐标(`y < 1 || y > m`)
再或者这一个格子不是水(`a[x][y] != 'W'`),
那么表明,这个格子不是我们要找的,于是返回。这时会返回到上一层,去在上一个已经确定好的点寻找周围的另一个,退回去。
a[x][y] = 'F';
v = true;
for (int i = 0; i < 8; ++i) {
int xx = x + dx[i];
int yy = y + dy[i];
if (a[xx][yy] == 'W')
dfs(xx, yy);
}
首先将这一格设为`'F'`,表示这一格已经走过了。然后将`v`改为`true`(后面有妙用),接着,用我们定义的数组,计算出周围的8格的坐标。
接着有一个`if`判断,判断下一个点是不是`'W'`如果是,进行下一轮深搜。
如果仅凭这个`dfs`,我们还无法实现深搜,一次主函数调用,他会向周围找到一个水潭,**但是我们应该如何确定这个水潭的坐标?**
这时候我们就要在主函数内采用一个双重循环查找每一个点,**当然,因为我们已经在查找每一次点时,就已经将他设为了`'F'`,所以一个水潭我们只会搜索到一遍。**
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> a[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (a[i][j] == 'W') {
dfs(i, j);
if (v) {
c++;
v = false;
}
}
cout << c;
return 0;
}
在主函数内,先输入`n, m`和Farmer John' s Field的示意图。**然后,进行深度优先搜索。**
双重循环搜索,找到第一个为`'W'`的点,进行洪水填充,**接下来,这一个水潭都已经改变了`'W'`,变成了`'F'`,所以这些点不会再次触发洪水填充。** 接下来判断`v`,也就是我们之前设为`true`的那个。他被用来判断这里是否有水。如果有,将`c`统计变量`++`,最后输出`c`,这个程序就结束了。
看完这个分析,是不是恍然大悟?深搜是很好理解的。
以下是源程序。
#include <iostream>
using namespace std;
int dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
int dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int n, m, c;
bool v;
char a[105][105];
void dfs(int x, int y) {
if (x < 1 || x > n || y < 1 || y > m || a[x][y] != 'W')
return ;
a[x][y] = 'F';
v = true;
for (int i = 0; i < 8; ++i) {
int xx = x + dx[i];
int yy = y + dy[i];
if (a[xx][yy] == 'W')
dfs(xx, yy);
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> a[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (a[i][j] == 'W') {
dfs(i, j);
if (v) {
c++;
v = false;
}
}
cout << c;
return 0;
}
以下是用于复制的程序
#include <iostream>
using namespace std;
int dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
int dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int n, m, c;
bool v;
char a[105][105];
void dfs(int x, int y) {
if (x < 1 || x > n || y < 1 || y > m || a[x][y] != 'W')
return ;
a[x][y] = 'F';
v = true;
for (int i = 0; i < 8; ++i) {
int xx = x + dx[i];
int yy = y + dy[i];
if (a[xx][yy] == 'W')
dfs(xx, yy);
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> a[i][j];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (a[i][j] == 'W') {
dfs(i, j);
if (v) {
c++;
v = false;
}
}
cout << c;
return 0;
}