题解:洛谷 P5727 冰雹猜想

【题目来源】

洛谷:P5727 【深基5.例3】冰雹猜想 - 洛谷

【题目描述】

给出一个正整数 \(n\),然后对这个数字一直进行下面的操作:如果这个数字是奇数,那么将其乘 \(3\) 再加 \(1\),否则除以 \(2\)。经过若干次循环后,最终都会回到 \(1\)。经过验证很大的数字 \((7\times 10^{11})\) 都可以按照这样的方式比变成 \(1\),所以被称为“冰雹猜想”。例如当 \(n\)\(20\),变化的过程是 \(20→10→5→16→8→4→2→1\)

根据给定的数字,验证这个猜想,并从最后的 1 开始,倒序输出整个变化序列。

【输入】

输入一个正整数 \(n\)

【输出】

输出若干个由空格隔开的正整数,表示从最后的 \(1\) 开始倒序的变化数列。

【输入样例】

20

【输出样例】

1 2 4 8 16 5 10 20

【核心思想】

  1. 问题分析:给定一个正整数 \(n\),按照冰雹猜想的规则(奇数则 \(n = 3n + 1\),偶数则 \(n = n / 2\))不断变化,直到 \(n\) 变为 \(1\)。需要倒序输出整个变化过程中产生的所有数字(从 \(1\) 开始到 \(n\))。本质是一个序列生成与反向输出问题,关键在于模拟过程并存储完整序列。

  2. 算法选择

    • 数组存储 + 倒序输出:使用数组按顺序存储从初始 \(n\)\(1\) 的整个变化序列,然后从最后一个元素(即 \(1\))开始向前遍历输出。这种方法简单直接,时间复杂度 \(O(\text{序列长度})\),空间复杂度 \(O(\text{序列长度})\)。由于序列长度通常较小(对于 \(n \le 10^5\),长度约在几十到几百之间),数组大小设为 5000 足够安全。
  3. 关键步骤

    • 读入与初始化:读取 \(n\),将 \(n\) 存入数组 a[1],并设置索引 mark = 2
    • 生成序列:使用 while (n != 1) 循环,根据 \(n\) 的奇偶性计算下一个值,存入数组,并更新索引。
    • 倒序输出:从 mark - 11 遍历数组,输出每个数字,数字之间用空格分隔。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(L)\),其中 \(L\) 是冰雹序列的长度,对于给定的数据范围,\(L\) 很小。
    • 空间复杂度:\(O(L)\),用于存储序列。
  5. 模拟与猜想验证

    • 操作模拟:严格按照题目规则生成序列,没有复杂的优化或数学证明需求。
    • 倒序输出:因为要求从 \(1\) 开始倒序输出,所以必须存储整个序列,然后反向遍历。
    • 数组大小:由于 \(n\) 在变化过程中可能会暂时变得很大(但题目给定的 \(n\) 通常不大),数组容量 5000 足以应对,如需更安全可使用 vector 动态存储。
    • 适用场景:这类“模拟过程并反向输出”的问题在入门编程中很常见,核心是掌握数组存储和反向遍历的技巧。

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

/**
 * 主函数 - 程序入口
 * @return 程序执行状态码(0表示成功)
 */
int main()
{
    int n;               // 定义变量:初始数字
    int a[5000];        // 定义数组:存储Collatz序列
    int mark = 1;       // 定义变量:数组索引标记,初始为1

    cin >> n;           // 输入初始数字
    a[mark] = n;        // 将初始数字存入数组
    mark++;             // 数组索引后移

    // 生成Collatz序列直到n变为1
    while (n != 1)      // 当n不等于1时继续循环
    {
        if (n % 2 == 0) // 如果n是偶数
            n /= 2;     // n除以2
        else            // 如果n是奇数
            n = n * 3 + 1;  // n乘以3加1

        a[mark] = n;    // 将当前数字存入数组
        mark++;         // 数组索引后移
    }

    // 倒序输出Collatz序列
    for (int i = mark - 1; i >= 1; i--)
    {
        cout << a[i] << " ";  // 输出当前数字
    }

    return 0;           // 程序正常结束
}

【运行结果】

20
1 2 4 8 16 5 10 20
posted @ 2026-08-31 09:27  团爸讲算法  阅读(27)  评论(0)    收藏  举报