Lake Counting

GitHub跳转原题关系图返回列表

八连通洪泛填充:扫描棋盘遇到未访问的 W 就计数,并用迭代栈把它所在整片连通水洼就地从 W 改写成 .。

OJ: roj

题目 ID: 1249

难度:普及-

标签:搜索洪泛填充连通块网格python

创建: 2026-09-30 01:45

更新: 2026-10-05 06:54

题目描述

给定 N×MN\times M 的棋盘,W 表示有水、. 表示干燥,八连通(上下、左右及四条对角线)的 W 属于同一片水洼,求水洼总数。输入第一行 N,MN,M(1≤N,M≤1101\le N,M\le110),其后 NN 行为棋盘,输出水洼数。样例输入为 10 12 后接 1010 行棋盘,样例输出 3。

思路

八连通不是四连通,斜着相邻也算同一片。外层扫描棋盘,每遇到一个仍为 W 的格子就说明发现一片新水洼:答案加一,再从它出发用迭代栈洪泛,把整片连通水洼全部改写成 .,棋盘因此兼任访问标记。总复杂度 O(NM)O(NM)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 01:46
# update_at: 2026-09-30 01:46

import sys

WATER = 'W'   # 有水
DRY = '.'     # 干燥;同时用它把已访问的水格抹掉,省掉额外的 visited 表
OFFSETS = tuple(
    (dr, dc)
    for dr in (-1, 0, 1)
    for dc in (-1, 0, 1)
    if (dr, dc) != (0, 0)
)  # 八连通:上、下、左、右 + 四条对角线,共 8 个方向


def flood(grid: list[bytearray], row: int, col: int) -> None:
    """从 (row, col) 出发做八连通洪泛,把整片水洼改成 DRY(原地标记已访问)。"""
    grid[row][col] = ord(DRY)
    stack = [(row, col)]
    while stack:
        r, c = stack.pop()
        for dr, dc in OFFSETS:
            nr, nc = r + dr, c + dc
            # 越界判定合并成一次链式比较,比 0 <= nr < n and 0 <= nc < m 少一趟比较
            if -1 < nr < len(grid) and -1 < nc < len(grid[0]) and grid[nr][nc] == ord(WATER):
                grid[nr][nc] = ord(DRY)
                stack.append((nr, nc))


def solve() -> None:
    """统计八连通 'W' 的连通块个数(水洼数)。"""
    data = iter(sys.stdin.buffer.read().split())
    rows = int(next(data))   # 题面的 N
    cols = int(next(data))   # 题面的 M:只为顺序消费掉,行宽以每行 token 本身为准
    grid = [bytearray(next(data)) for _ in range(rows)]  # 每行是一个整体 token

    ponds = 0
    for r in range(rows):
        for c in range(len(grid[r])):
            if grid[r][c] == ord(WATER):
                ponds += 1          # 每遇到一个尚未被洪泛吞掉的水格,就开出一片新水洼
                flood(grid, r, c)

    print(ponds)


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 06:55
 * update_at: 2026-10-05 06:55
 */
#include <cstdio>

typedef long long ll;

const int MAXN = 115;

char grid[MAXN][MAXN];    // grid[1..n][1..m]:'W' 有水、'.' 干燥;访问过的 'W' 就地改成 '.' 当访问标记
int stack_r[MAXN * MAXN]; // 洪泛栈的行坐标,每格最多入栈一次,容量 n*m 足够
int stack_c[MAXN * MAXN]; // 洪泛栈的列坐标

ll n, m;

int main() {
    scanf("%lld %lld", &n, &m);
    for (ll i = 1; i <= n; i++) {
        scanf("%s", grid[i] + 1);
    }

    ll ponds = 0;
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= m; j++) {
            if (grid[i][j] != 'W') {
                continue;
            }
            ponds++; // 遇到一个还没被洪泛吞掉的水格,就是一片新水洼
            // 迭代洪泛:栈里放待扩展的格子坐标
            ll top = 0;
            grid[i][j] = '.'; // 入栈前就标记,保证每个水格最多入栈一次
            stack_r[top] = i;
            stack_c[top] = j;
            top++;
            while (top > 0) {
                top--;
                ll r = stack_r[top];
                ll c = stack_c[top];
                // 八连通:行、列偏移各取 -1,0,1,排除 (0,0)
                for (ll dr = -1; dr <= 1; dr++) {
                    for (ll dc = -1; dc <= 1; dc++) {
                        if (dr == 0 && dc == 0) {
                            continue;
                        }
                        ll nr = r + dr;
                        ll nc = c + dc;
                        if (nr < 1 || nr > n || nc < 1 || nc > m) {
                            continue;
                        }
                        if (grid[nr][nc] != 'W') {
                            continue;
                        }
                        grid[nr][nc] = '.';
                        stack_r[top] = nr;
                        stack_c[top] = nc;
                        top++;
                    }
                }
            }
        }
    }

    printf("%lld\n", ponds);
    return 0;
}