题解:洛谷 AT_abc464_b Crop

【题目来源】

洛谷:AT_abc464_b [ABC464B] Crop - 洛谷

【题目描述】

There is a black-and-white image of height \(H\) pixels and width \(W\) pixels. The color of the pixel at the \(i\)-th row from the top and \(j\)-th column from the left is given as a character \(C_{i,j}\), where . represents white and # represents black.

From the top, bottom, left, and right edges of this image, remove rows and columns where all pixels are white. Specifically, perform the following operations in order:

  1. While the topmost row of the image is entirely white, repeat removing the topmost row.
  2. While the bottommost row of the image is entirely white, repeat removing the bottommost row.
  3. While the leftmost column of the image is entirely white, repeat removing the leftmost column.
  4. While the rightmost column of the image is entirely white, repeat removing the rightmost column.

Output the image after processing.

The given image contains at least one black pixel.

有一张高度为 \(H\) 像素、宽度为 \(W\) 像素的黑白图像。从上往下第 \(i\) 行、从左往右第 \(j\) 列的像素颜色用字符 \(C_{i,j}\) 表示,其中 . 表示白色,# 表示黑色。

从该图像的上、下、左、右边缘,移除所有像素均为白色的行和列。具体按以下顺序执行操作:

  1. 当图像最上面一行全部为白色时,重复移除最上面一行。
  2. 当图像最下面一行全部为白色时,重复移除最下面一行。
  3. 当图像最左面一列全部为白色时,重复移除最左面一列。
  4. 当图像最右面一列全部为白色时,重复移除最右面一列。

输出处理后的图像。

给定的图像中至少包含一个黑色像素。

【输入】

The input is given from Standard Input in the following format:

\(H\) \(W\)
\(C_{1,1}C_{1,2}\ldots C_{1,W}\)
\(C_{2,1}C_{2,2}\ldots C_{2,W}\)
\(\vdots\)
\(C_{H,1}C_{H,2}\ldots C_{H,W}\)

【输出】

Output the image after processing in the following format.
Here, \(h\) and \(w\) are the height and width of the image after processing, in pixels, respectively.
\(c_{i,j}\) is the character representing the color of the pixel at the \(i\)-th row from the top and \(j\)-th column from the left; \(c_{i,j}\) must be . if the pixel is white and # if it is black.

\(c_{1,1}c_{1,2}\ldots c_{1,w}\)
\(c_{2,1}c_{2,2}\ldots c_{2,w}\)
\(\vdots\)
\(c_{h,1}c_{h,2}\ldots c_{h,w}\)

【输入样例】

4 5
.....
..#..
.###.
.....

【输出样例】

.#.
###

【核心思想】

  1. 问题分析:给定一个 \(H \times W\) 的黑白图像(. 表示白色,# 表示黑色),需要按顺序从四个边缘(上、下、左、右)移除所有全白的行或列,直到每个边缘遇到第一个非全白的行/列为止。由于保证至少有一个黑色像素,最终图像非空。本质上是在二维矩阵中定位包含黑色像素的最小行区间和列区间。

  2. 算法选择

    • 直接模拟:按照题目要求的顺序,逐边检查并标记全白行/列
    • 边界收缩:通过四个方向的扫描确定保留区域的上边界 \(top\)、下边界 \(bottom\)、左边界 \(left\)、右边界 \(right\)
  3. 关键步骤

    • 读取图像:读入 \(H\)\(W\) 和像素矩阵 \(C[1..H][1..W]\)
    • 确定上边界 \(top\):从第 \(1\) 行开始向下扫描,找到第一个不全白的行
    • 确定下边界 \(bottom\):从第 \(H\) 行开始向上扫描,找到第一个不全白的行
    • 确定左边界 \(left\):从第 \(1\) 列开始向右扫描,找到第一个不全白的列
    • 确定右边界 \(right\):从第 \(W\) 列开始向左扫描,找到第一个不全白的列
    • 输出保留区域:输出 \(C[i][j]\),其中 \(i \in [top, bottom]\)\(j \in [left, right]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(H \times W)\),每个像素最多被检查常数次(行扫描和列扫描)
    • 空间复杂度:\(O(H \times W)\),存储原始图像矩阵
  5. 模拟与边界收缩的核心思想

    • 方向独立性:四个边缘的移除操作互不影响最终结果(因为全白行/列的移除不会使内部的非全白行/列变成全白),因此可以直接计算四个边界而不必真的逐行逐列删除
    • 最小包围矩形:问题等价于找到包含所有黑色像素的最小轴对齐矩形,四个边界分别由最上、最下、最左、最右的黑色像素位置决定
    • 顺序无关性优化:虽然题目规定了上→下→左→右的顺序,但由于黑色像素的位置固定,实际边界结果与处理顺序无关,可独立计算四个边界后统一输出
    • 适用于二维矩阵的边缘裁剪、最小包围区域定位等基础图像处理问题

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 55;               // 最大图像尺寸
int h, w;                       // h: 图像高度, w: 图像宽度
char a[N][N];                   // a[i][j]: 原始图像第i行第j列的像素
int b[N][N];                    // b[i][j]: 标记该像素是否被移除(1表示移除,0表示保留)

int main()
{
    cin >> h >> w;              // 读入图像高度和宽度

    // 读入图像
    for (int i = 1; i <= h; i++)
        for (int j = 1; j <= w; j++)
            cin >> a[i][j];

    // ========== 第一步:移除顶部全白行 ==========
    bool ok = true;
    while (ok)
    {
        // 从上往下检查每一行
        for (int i = 1; i <= h; i++)
        {
            bool flag = 1;      // flag=1表示当前行全白

            // 检查第i行是否全白
            for (int j = 1; j <= w; j++)
            {
                if (a[i][j] == '#')     // 发现黑色像素
                {
                    flag = 0;           // 不是全白行
                    break;
                }
            }

            if (flag)               // 第i行全白,标记移除
            {
                for (int j = 1; j <= w; j++)
                    b[i][j] = 1;    // 标记该行为移除
            }
            else                    // 第i行不全白,停止检查(只移除连续顶部的全白行)
            {
                ok = false;
                break;
            }
        }
    }

    // ========== 第二步:移除左侧全白列 ==========
    ok = true;
    while (ok)
    {
        // 从左往右检查每一列
        for (int j = 1; j <= w; j++)
        {
            bool flag = 1;          // flag=1表示当前列全白

            // 检查第j列是否全白
            for (int i = 1; i <= h; i++)
            {
                if (a[i][j] == '#')     // 发现黑色像素
                {
                    flag = 0;           // 不是全白列
                    break;
                }
            }

            if (flag)               // 第j列全白,标记移除
            {
                for (int i = 1; i <= h; i++)
                    b[i][j] = 1;    // 标记该列为移除
            }
            else                    // 第j列不全白,停止检查
            {
                ok = false;
                break;
            }
        }
    }

    // ========== 第三步:移除底部全白行 ==========
    ok = true;
    while (ok)
    {
        // 从下往上检查每一行
        for (int i = h; i >= 1; i--)
        {
            bool flag = 1;          // flag=1表示当前行全白

            // 检查第i行是否全白
            for (int j = 1; j <= w; j++)
            {
                if (a[i][j] == '#')     // 发现黑色像素
                {
                    flag = 0;           // 不是全白行
                    break;
                }
            }

            if (flag)               // 第i行全白,标记移除
            {
                for (int j = 1; j <= w; j++)
                    b[i][j] = 1;    // 标记该行为移除
            }
            else                    // 第i行不全白,停止检查
            {
                ok = false;
                break;
            }
        }
    }

    // ========== 第四步:移除右侧全白列 ==========
    ok = true;
    while (ok)
    {
        // 从右往左检查每一列
        for (int j = w; j >= 1; j--)
        {
            bool flag = 1;          // flag=1表示当前列全白

            // 检查第j列是否全白
            for (int i = 1; i <= h; i++)
            {
                if (a[i][j] == '#')     // 发现黑色像素
                {
                    flag = 0;           // 不是全白列
                    break;
                }
            }

            if (flag)               // 第j列全白,标记移除
            {
                for (int i = 1; i <= h; i++)
                    b[i][j] = 1;    // 标记该列为移除
            }
            else                    // 第j列不全白,停止检查
            {
                ok = false;
                break;
            }
        }
    }

    // ========== 输出处理后的图像 ==========
    for (int i = 1; i <= h; i++)
    {
        for (int j = 1; j <= w; j++)
        {
            if (!b[i][j])           // 只输出未被移除的像素
                cout << a[i][j];
        }
        cout << endl;               // 每行结束换行
    }

    return 0;
}

【运行结果】

4 5
.....
..#..
.###.
.....
.#.
###
posted @ 2026-06-30 21:15  团爸讲算法  阅读(18)  评论(0)    收藏  举报