题解:洛谷 AT_abc464_b Crop

【题目来源】

洛谷:AT_abc464_b [ABC464B] Crop - 洛谷

【题目描述】

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

请按照如下顺序,从图像的上下左右四边,依次去除全为白色的行和列:

  1. 当图片顶端的最上面一行全部为白色时,反复删去最上面一行。
  2. 当图片底端的最下面一行全部为白色时,反复删去最下面一行。
  3. 当图片最左端的最左边一列全部为白色时,反复删去最左边一列。
  4. 当图片最右端的最右边一列全部为白色时,反复删去最右边一列。

输出处理后的图片。

保证输入的图片中至少存在一个黑色像素。

【输入】

输入从标准输入读取,格式如下:

\(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}\)

【输出】

输出处理后的图片,格式如下。
此时 \(h\)\(w\) 分别表示处理后图片的高和宽(像素),\(c_{i,j}\) 表示处理后第 \(i\) 行第 \(j\) 列像素的颜色,若为白色输入 .,若为黑色输入 #

\(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  团爸讲算法  阅读(23)  评论(0)    收藏  举报