Skip to content

好的主人,请看这篇为 B. osu!mania 准备的题解喵~!希望能帮到你哟!

B. osu!mania - 和猫娘一起打音游喵~

题目大意

主人,你正在玩一个叫做 osu!mania 的节奏游戏,喵~ 游戏的谱面是一个 n4 列的网格。音符会从上往下掉落,所以我们总是先处理最下面的音符,然后依次向上处理,直到最上面的音符。

每一行都有且仅有一个音符,用 '#' 表示。游戏开始时,你需要按照处理的顺序(也就是从下到上的顺序),依次说出每个音符所在的列号(列号从 1 到 4)。

举个例子,如果谱面长这样:

#...  (第1行)
.#..  (第2行)
..#.  (第3行)
...#  (第4行)

我们是从下往上处理的,所以:

  1. 第一个处理的是第 4 行的音符,它在第 4 列。
  2. 第二个处理的是第 3 行的音符,它在第 3 列。
  3. 第三个处理的是第 2 行的音符,它在第 2 列。
  4. 第四个处理的是第 1 行的音符,它在第 1 列。

所以,主人你应该输出 4 3 2 1。明白了吗喵?


解题思路

这个问题其实有一个小小的“陷阱”哦,就像猫猫假装对逗猫棒没兴趣,然后突然扑上去一样!(ฅ´ω`ฅ)

我们来梳理一下:

  1. 输入顺序:程序读入数据时,是从第 1 行(最上面一行)读到第 n 行(最下面一行)。
  2. 处理顺序:游戏里击打音符的顺序,是从第 n 行(最下面一行)到第 1 行(最上面一行)。

看到了吗喵?输入顺序和处理顺序正好是相反的!

所以,我们的策略就很清晰啦:

  1. 先把所有行的音符位置都记录下来。我们可以用一个数组或者 vector,按照从上到下的顺序,依次存储每一行 '#' 所在的列号。
  2. 当所有行的信息都读取并存储完毕后,我们再倒序输出我们记录的列号。这样就完美地模拟了从下到上的处理顺序啦!

就像先把所有小鱼干都摆在面前,然后再从离自己最近的那个开始吃,嘿嘿~


题解代码分析

下面是解题代码,猫娘我加上了一些注释,方便主人理解每一句都在做什么哦。

cpp
#include <iostream>
#include <vector>
#include <string>

/**
 * @brief 解决单个测试用例的函数喵~
 */
void solve() {
    int n;
    std::cin >> n; // 先读入谱面有多少行,n

    // 我们准备一个小篮子(vector),用来按顺序存放每一行音符的列号
    // 输入是从上到下给的,所以我们也是从上到下地把列号放进篮子
    std::vector<int> note_columns(n);

    // 循环 n 次,一行一行地读取谱面
    for (int i = 0; i < n; ++i) {
        std::string row_str;
        std::cin >> row_str; // 读取这一行的4个字符,比如 ".#.."

        // 在这一行里找到 '#' 小鱼干的位置!
        for (int j = 0; j < 4; ++j) {
            if (row_str[j] == '#') {
                // 找到了!它的列号是 j+1 (因为数组下标从0开始,而列号从1开始)
                note_columns[i] = j + 1;
                // 每一行只有一个音符,找到了就可以不用再找了,直接看下一行,喵~
                break;
            }
        }
    }

    // 最关键的一步来啦!我们要倒着把篮子里的东西拿出来
    // 因为我们是从下往上(也就是从第 n 个到第 1 个)打歌的嘛~
    for (int i = n - 1; i >= 0; --i) {
        std::cout << note_columns[i];
        // 为了格式好看,除了最后一个数字,其他数字后面都要加个空格
        if (i > 0) {
            std::cout << " ";
        }
    }
    // 一个测试用例处理完啦,换行一下~
    std::cout << "\n";
}

int main() {
    // 这两行是加速输入输出的魔法咒语,让程序跑得更快哦!
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int t;
    std::cin >> t; // 读入有多少个测试用例
    while (t--) {
        solve(); // 一个一个解决
    }

    return 0;
}

相关知识点介绍

主人,这个问题虽然简单,但也用到了几个很有用的小知识呢,让猫娘来给你介绍一下吧!

  1. std::vector(动态数组)

    • 在 C++ 中,std::vector 是一个非常有用的容器,你可以把它想象成一个可以自动伸缩的数组。你不需要在一开始就确定它的大小,可以随时向里面添加或删除元素。
    • 在这道题里,我们用 std::vector<int> note_columns(n); 创建了一个能存放 n 个整数的 vector,用来存储每一行音符的列号,非常方便喵!
  2. std::string(字符串)

    • std::string 用来处理文本数据。在这道题里,我们用它来读取谱面的每一行(例如 ".#..")。
    • 我们可以像访问数组一样,用 [] 来访问字符串中的单个字符,比如 row_str[j] 就是访问第 j 个字符。
  3. 逆序处理逻辑

    • 这道题的核心思想!很多问题都会有类似“正序输入,逆序处理”的模式。学会先将信息完整地存储下来,再按照需要的顺序进行处理,是一种非常重要的编程思维。下次遇到类似的问题,主人一定能一眼看穿的!
  4. 加速输入输出

    • std::ios_base::sync_with_stdio(false);std::cin.tie(NULL); 是 C++ 竞赛中常用的两行代码。它们可以解除 C++ 的 iostream 和 C 的 stdio 之间的同步,并解开 cincout 的绑定,从而大幅提升输入输出的效率。在处理大量数据时,这个小技巧能避免因为I/O过慢而超时,是个很有用的小魔法哦!

好啦,这次的题解就到这里啦!主人是不是完全明白了呢?如果还有问题,随时可以再来问我哦!喵~ (ฅ^•ﻌ•^ฅ)

Released under the MIT License.