Skip to content

喵~ 各位算法大师们好呀!咱是猫娘 Nya-ko,今天也要元气满满地解决一道有趣的题目哦!这次我们要看的题目是 Codeforces 上的 "Nearly Lucky Number",一起来看看 Petya 的幸运数字游戏吧!

A. Nearly Lucky Number

题目大意

Petya 有个特别的爱好,就是喜欢“幸运数字”。所谓的“幸运数字”,就是那些只由“幸运数位”(也就是 4 和 7)组成的数字。比如说,47、744、4 都是幸运数字,而 5、17、467 就不是啦。

但是呢,不是所有数字都那么幸运。于是 Petya 又想出了一个概念,叫做“近似幸运数”。一个数如果它里面包含的幸运数位(4 或 7)的总个数本身是一个幸运数字,那么这个数就是“近似幸运数”。

现在的问题是,给你一个整数 n,请你判断它是不是一个“近似幸运数”呢?

举个例子来理解一下,喵~

  • 40047:这个数里有 447 这三个幸运数位。总数是 3。数字 3 本身不是一个幸运数字(因为它含有数位 3),所以 40047 不是近似幸运数。
  • 7747774:这个数里有 7 个幸运数位。总数是 7。数字 7 本身是一个幸运数字,所以 7747774 是一个近似幸运数。

很简单对吧?那我们开始分析怎么解决它吧!

解题方法

这个问题看起来有两层定义,有点绕,但只要我们一步步拆解,就会发现它其实是一只很温顺的小猫咪哦!

  1. 处理输入:首先,题目告诉我们 n 最大可以到 101810^{18}。这么大的数字,用 C++ 的 long long 刚好可以存下,但其实还有更简单的方法!因为我们只关心每一位上的数字是什么,所以直接把它当成 字符串(string) 来读入会更方便处理,喵~

  2. 统计幸运数位:读入字符串后,我们就要数一数里面有多少个 '4' 和 '7' 啦。这很简单,只需要遍历一遍字符串,用一个计数器 lucky_digit_count 记录下来就好。

  3. 关键的发现:现在我们得到了幸运数位的总数 lucky_digit_count。接下来就是要判断这个 count 本身是不是一个“幸运数字”。 等一下!n 最多有 18 位(对于 1018110^{18}-1)或者 19 位(对于 101810^{18} 本身)。那么 lucky_digit_count 的最大值也就不超过 19。 所以,我们只需要判断在 1 到 19 这个小范围内的数字,哪些是“幸运数字”就可以啦!

    我们来找找看:

    • 只包含 4 和 7 的正整数。
    • 在 [1, 19] 这个范围里... 嗯... 只有 47 本身符合条件!
  4. 最终判断:所以,整个问题就简化成了一个超级简单的问题:统计完输入数字中 '4' 和 '7' 的总个数后,判断这个总数是不是等于 4 或者 7?

    • 如果是,那就输出 "YES"。
    • 如果不是,那就输出 "NO"。

是不是一下子就清晰明了了呢?喵哈哈~

题解 - C++

下面是具体的实现代码,咱加了一些注释,方便你理解哦!

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

// 题目要求我们判断一个数 n 是不是 "近似幸运数"
// “近似幸运数”的定义是:它所包含的幸运数位('4' 和 '7')的总数,本身是一个“幸运数字”
// “幸运数字”的定义是:只由 '4' 和 '7' 组成的数字

// 解题思路就是:
// 1. 把输入的 n 当作字符串读进来,因为 n 很大,而且我们需要遍历它的每一位。
// 2. 统计字符串中 '4' 和 '7' 的数量,记作 lucky_digit_count。
// 3. 判断 lucky_digit_count 是不是一个幸运数字。
// 4. 关键点:因为 n 最多 19 位,所以 lucky_digit_count 的范围是 0 到 19。
// 5. 在 0-19 这个范围内,符合“幸运数字”定义的只有 4 和 7。
// 6. 所以,问题就简化为:判断 lucky_digit_count 是不是等于 4 或者 7。

int main() {
    // 使用 stdio 加速,让程序跑得更快,喵~
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    // 用字符串来装这个超——大的数字 n
    std::string n_str;
    std::cin >> n_str;

    // 准备一个计数器,来数一数有多少个幸运数位
    int lucky_digit_count = 0;
    
    // 遍历字符串里的每一个字符呀
    for (char digit : n_str) {
        // 如果是幸运数位 '4' 或者 '7'
        if (digit == '4' || digit == '7') {
            // 计数器就 +1 啦
            lucky_digit_count++;
        }
    }

    // 看看幸运数位的总数是不是 4 或者 7,这可是解题的关键哦!
    if (lucky_digit_count == 4 || lucky_digit_count == 7) {
        std::cout << "YES\n";
    } else {
        std::cout << "NO\n";
    }

    return 0;
}

知识点介绍

这道题虽然简单,但里面包含了一些在算法竞赛中非常有用的基础知识点呢,让咱来给你梳理一下~

  1. 大数处理 (Large Number Handling)

    • 当题目给出的数字范围超过了 long long(大约是 9×10189 \times 10^{18})的表示范围,或者我们需要对数字的每一位进行操作时,将数字作为 字符串 读入是一种非常常用且强大的技巧。这样可以轻松地访问到每一位,而不用做复杂的取模和除法运算。
  2. 字符串遍历 (String Traversal)

    • 在 C++11 及之后的版本中,使用基于范围的 for 循环 (for (char c : str)) 是遍历字符串(或任何容器)最简洁、最安全的方式。它避免了使用下标可能导致的边界错误,代码也更易读。
  3. 分析问题约束 (Analyzing Constraints)

    • 这是解决算法问题中一个超级重要的能力!本题的核心突破口就在于分析了输入 n 的范围。正是因为 n 最大为 101810^{18},我们才能推断出幸运数位的总数 lucky_digit_count 不会超过 19,从而将一个看似复杂的问题(判断一个数是否为幸运数)简化成了一个简单的 if 判断。在面对任何问题时,都要先仔细看看输入数据的范围,里面常常藏着解题的钥匙哦!

好啦,今天的题解就到这里结束啦!希望能帮到你,喵~ 如果还有其他问题,随时可以来找咱玩哦!

Released under the MIT License.