跳转至

语言月赛 202411 Enemy

题目概述

给定一个 n × m 的字符矩阵,代表战场局面,包含:

  • A:A 城士兵;
  • B:B 城士兵;
  • H:城墙;
  • #:空地。

每一行,A 城士兵总在左,B 城士兵在右,若存在城墙 H,必然在两者中间,且每行最多一个 H

目标

模拟战斗全过程,最终输出战场的局势。


战斗阶段一:每行独立处理

对每一行进行处理,按照以下规则:

情况 1:本行存在城墙(防守)

  • 双方各自撤回至己方边界,按顺序排列。
  • A 城士兵靠左紧凑排列;
  • B 城士兵靠右紧凑排列;
  • 城墙 H 保留;
  • 其余位置填充为 #

例如:

输入: #A#AH#B# 输出: AA##H##B

情况 2:无城墙(进攻)

  • 若 A 与 B 人数相等:双方全部消失。
  • 若 A 多:B 全部消失,A 全员进攻靠右排列;
  • 若 B 多:A 全部消失,B 全员进攻靠左排列;
  • 其余位置填充为 #

例如:

输入: #A##B#B# 输出: BB######


战斗阶段二:士兵消失判定

在第一阶段后,进行“邻接士兵”判断。

对于战场中剩余的 AB

  • 如果它上方或下方与其相邻列有相同士兵(即“连线”),则它将消失
  • 所有士兵的判断是同时进行的。

注意:判断只考虑上下方向,左右方向无影响。

例如:

输入:

AAA

BBB

中间行无士兵,因此上、下方士兵不会消失。


实现思路总结

  1. 逐行统计每行的 A/B 数量和城墙位置;
  2. 根据是否有城墙决定处理逻辑(防守或进攻);
  3. 使用二维数组存战场;
  4. 统一处理所有行后,进行“上下邻接判定”处理;
  5. 最终输出结果矩阵。

代码实现

#include <bits/stdc++.h>
using namespace std;

// 定义最大矩阵维度常量
constexpr int N = 1e3 + 5;

int n, m;         // n 行,m 列
char a[N][N];     // 战场地图矩阵

int main()
{
    // 输入矩阵大小
    cin >> n >> m;

    // 输入战场局势,注意从 1 开始编号,便于处理边界
    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++)
    {
        int y = 0;            // 城墙所在位置,若为 0 表示没有城墙
        int numa = 0, numb = 0;  // A/B 士兵计数

        // 统计该行的 A/B 数量,并找出城墙位置
        for (int j = 1; j <= m; j++)
        {
            if (a[i][j] == 'H')
                y = j;
            if (a[i][j] == 'A')
                numa++;
            if (a[i][j] == 'B')
                numb++;
        }

        if (y) // 有城墙,双方撤退
        {
            // 左侧 numa 个 A 士兵
            for (int j = 1; j <= numa; j++)
                a[i][j] = 'A';
            // 城墙左边多余的位置置为 #
            for (int j = numa + 1; j < y; j++)
                a[i][j] = '#';
            // 右侧 numb 个 B 士兵,从右边向左排列
            for (int j = m; j >= m - numb + 1; j--)
                a[i][j] = 'B';
            // 城墙右边多余的位置置为 #
            for (int j = y + 1; j <= m - numb; j++)
                a[i][j] = '#';
        }
        else // 无城墙,双方进攻
        {
            if (numa == numb)
            {
                // A 和 B 数量相等,全灭
                for (int j = 1; j <= m; j++)
                    a[i][j] = '#';
            }
            else if (numa > numb)
            {
                // A 多,B 灭,A 向右推进
                for (int j = m; j >= m - numa + 1; j--)
                    a[i][j] = 'A';
                for (int j = m - numa; j >= 1; j--)
                    a[i][j] = '#';
            }
            else
            {
                // B 多,A 灭,B 向左推进
                for (int j = 1; j <= numb; j++)
                    a[i][j] = 'B';
                for (int j = numb + 1; j <= m; j++)
                    a[i][j] = '#';
            }
        }
    }

    // 第二阶段:上下相邻同类士兵消失
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (a[i][j] == 'A' || a[i][j] == 'B')
            {
                int nx = i + 1, ny = j;  // 下方的格子
                bool ok = 0;

                // 向下查找是否有相邻同类士兵
                while (nx <= n && a[nx][ny] == a[i][j])
                {
                    ok = 1;             // 标记可以消失
                    a[nx][ny] = '#';    // 下方士兵消失
                    nx++;
                }

                if (ok)
                    a[i][j] = '#';      // 当前士兵也消失
            }
        }
    }

    // 输出最终战场局势
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << a[i][j];
        }
        cout << "\n";
    }

    return 0;
}

复杂度分析

  • 时间复杂度:$O(n \cdot m)$;
  • 空间复杂度:$O(n \cdot m)$。

小技巧提示

  • 预处理时记录每行 A/B 数目和城墙位置;
  • 注意最后的“士兵消失”是同时进行的;
  • 矩阵从 $1$ 开始编号是为了便于边界处理。

总结

这是一道模拟题,细节较多,关键在于:

  • 分阶段进行模拟;
  • 精确实现规则;
  • 合理控制边界和同时判断的逻辑。

熟练掌握模拟类题目的处理方式,是刷题提升综合能力的重要一环。