红与黑

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

把黑砖看成网格图的顶点,答案是起点所在连通分量的大小;一次洪水填充 O(WH) 求出。

OJ: roj

题目 ID: 1216

难度:普及-

标签:搜索深度优先搜索广度优先搜索网格连通性python

创建: 2026-09-30 00:01

更新: 2026-10-05 05:42

题目描述

多组数据,读到 0 0 结束:每组给出 W,HW,H(均不超过 20)和 HH 行、每行 WW 个字符的网格,. 是黑砖、# 是红砖、@ 是黑砖且是你所站的位置。只能上下左右移动到相邻黑砖,求能到达的黑砖数(含起点)。

样例输入:第一行 6 9,随后 9 行依次是 ....#.、.....#、......、......、......、......、......、#@...#、.#..#.,最后一行 0 0。样例输出:45。

思路

黑砖是顶点、四方向相邻是边、红砖不进图,答案就是起点所在连通分量的大小,与走的路线无关。从 @ 出发做一次洪水填充,每块黑砖入栈时就染色成红砖,保证每个格子只被计数一次。

参考代码

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 00:01
# update_at: 2026-09-30 00:01

import sys

BLACK = '.'  # 可走的黑色瓷砖
START = '@'  # 黑色瓷砖,且是出发点
DIRS = ((-1, 0), (1, 0), (0, -1), (0, 1))  # 上、下、左、右四个相邻方向
TILES = frozenset('.#@')  # 瓷砖字符;用它把数字流和网格字符流分开


def count_reachable(grid: list[str], h: int, w: int, start: tuple[int, int]) -> int:
    """从 start 出发能到达的黑砖数(含起点):一次洪水填充,走过的格子染色不再回访。"""
    board = [list(row) for row in grid]  # 就地染色需要可变副本
    board[start[0]][start[1]] = '#'  # 起点即已访问
    stack = [start]
    count = 0
    while stack:
        r, c = stack.pop()
        count += 1
        for dr, dc in DIRS:
            nr, nc = r + dr, c + dc
            # 先判在界内,短路顺序保证越界时不会读数组
            if 0 <= nr < h and 0 <= nc < w and board[nr][nc] == BLACK:
                board[nr][nc] = '#'  # 入栈即标记,同一点不会被重复压栈
                stack.append((nr, nc))
    return count


def solve() -> None:
    text = sys.stdin.buffer.read().decode()
    sizes = iter(int(token) for token in text.split() if token.isdigit())  # 只有 W、H 是数字
    tiles = [ch for ch in text if ch in TILES]  # 网格字符跨行连续,按原始顺序排好
    out: list[str] = []
    pos = 0

    while True:
        w, h = next(sizes), next(sizes)  # x 方向、y 方向瓷砖数
        if w == 0 and h == 0:  # "0 0" 表示输入结束
            break
        # 连续取 h*w 个瓷砖字符,再按每行 w 个切成网格
        grid = [''.join(tiles[pos + r * w:pos + (r + 1) * w]) for r in range(h)]
        pos += h * w
        # 起点字符 '@' 在每个数据集合中唯一出现一次
        start = next((r, c) for r in range(h) for c in range(w) if grid[r][c] == START)
        out.append(str(count_reachable(grid, h, w, start)))

    print('\n'.join(out))


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 05:42
 * update_at: 2026-10-05 05:42
 */

// 红与黑:从 @ 出发做一次洪水填充,统计起点所在连通分量的黑砖数(含起点)。
#include <cstdio>

typedef long long ll;

const int MAXN = 25;
const int DIRS[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上、下、左、右四个相邻方向

char grid[MAXN][MAXN]; // 网格,已访问的黑砖就地染色成 '#',不需要额外的 visited 数组
ll stack_r[MAXN * MAXN]; // 待展开格子的行坐标
ll stack_c[MAXN * MAXN]; // 待展开格子的列坐标

// 从 (sr, sc) 出发洪水填充,返回能到达的黑砖数(包含起点)
ll flood_fill(ll h, ll w, ll sr, ll sc) {
    ll top = 0;
    stack_r[top] = sr;
    stack_c[top] = sc;
    top++;
    grid[sr][sc] = '#'; // 入栈即染色,保证同一个点不会被重复压栈

    ll count = 0;
    while (top > 0) {
        top--;
        ll r = stack_r[top];
        ll c = stack_c[top];
        count++; // 弹出即计数,每个可达黑砖恰好出栈一次
        for (ll d = 0; d < 4; d++) {
            ll nr = r + DIRS[d][0];
            ll nc = c + DIRS[d][1];
            if (nr < 0 || nr >= h || nc < 0 || nc >= w) continue; // 越界
            if (grid[nr][nc] != '.') continue; // 不是可走的黑砖
            grid[nr][nc] = '#';
            stack_r[top] = nr;
            stack_c[top] = nc;
            top++;
        }
    }
    return count;
}

int main() {
    while (1) {
        ll w, h;
        scanf("%lld %lld", &w, &h);
        if (w == 0 && h == 0) break; // 一行读入两个零表示输入结束

        ll sr = -1, sc = -1; // 起点 @ 的坐标,每个数据集合中唯一出现一次
        for (ll r = 0; r < h; r++) {
            scanf("%s", grid[r]);
            for (ll c = 0; c < w; c++) {
                if (grid[r][c] == '@') {
                    sr = r;
                    sc = c;
                }
            }
        }

        printf("%lld\n", flood_fill(h, w, sr, sc));
    }
    return 0;
}