洛谷:B3996:小洛的数字游戏


洛谷:B3996:小洛的数字游戏

题目

B3996:小洛的数字游戏

分析

本题难度正如题目所称,是标准的GESP 3级难度,涉及了循环、分支、数组的综合应用,同时还有很多坑需要注意避免。

首先,需要分析清楚题意并在脑海中/笔记上反应出相应的核心算法和过程。本题的核心算法和过程是什么呢?

flowchart TD A["当前数为n"]--> B["获得最右(也就是个位)数x,并处理:平方后取个位数=>x"]--> C["保存剩下的那些数字:remain"]--> D["凑出形如“x连上remain”的一个数字"]--> E["判定n==m?"]

获得一个数字的个位数很简单:x = n % 10,然后继续处理也很简单:x = (x * x) % 10,获得剩下的数字也很简单:remain = n/10

难点1:如何拼接

考虑一般情况,如何拼出形如x连上remain的一个数字呢?考虑一个例子:

n = 1234
x = (4 * 4) % 10 = 6
remain = 123

结果应该是6123。这里的洞见是:这个新的\(n = x \times 10^{remain的位数} + remain\)。于是,问题归结到如何求出remain的位数——而这,是有标准过程的。

int getRemainNumberLength(int remain)
{
    int len = 0;
    while (remain > 0)
    {
        remain /= 10;
        len++;
    }
    return len;
}

我们在这里没有考虑remain为零的特殊情况。在我们的算法中,如果remain == 0,会直接返回0,但实际上0也是一位数啊!

难点2:控制输出

这题的输出比较特殊。如果能在q步以内达到n == m,那么就要输出中间过程,直到n == m时结束;如果不能,那么只要输出-1。所以,我们需要存储所有中间结果,并设置一个found的标记(俗称flag),将其初始化为false

这又涉及一个比较标准的操作。对于这种找到、没找到的问题,其一般处理是:

  • 如果找到,found = true,并设置其他相关变量,同时跳出循环。
  • 如果一直没找到,那么found就肯定是false
  • 循环结束后,根据found进行不同的处理。
if (!found)
{
    cout << -1 << endl;
}
else
{
    for (int i = 0; i <= location; i++)
    {
        cout << result[i] << endl;
    }
}

陷阱1:是否得到了m的判定

这题有一个小小陷阱。其实不管x是不是为0,都要判断n == mx == 0只会影响数字的拼接过程,不会影响新得到的数字与m的比较过程。所以,我们的成功判定,必须在构造完新的数字之后,而且在判定之前先将这个新数字先放进中间结果的数组中去。

if (x == 0)
{
    n /= 10;
}
else
{
    // remain有d位
    // x*(10^d)+remain
    // 新的数字构成
    int len = getRemainNumberLength(remain);
    n = x * pow(10, len) + remain;
}

result[i] = n;

if (n == m)
{
    found = true;
    location = i;
    break;
}

完整代码见下图。

答案

Solution

思考

需要提一句的是,我们这里直接用n = x * pow(10, len) + remain;计算新的数字,但pow是个浮点数运算函数,可能存在一些误差。不过,在本题中,这个问题问题并未出现。更好的方法,是直接用个小循环或整数幂函数计算\(10^{len}\)(例如累乘或预计算表)。

如前所述,本题是典型的 GESP 3级编程题,涉及循环、分支与数组等基本语法,同时也考察对边界与实现细节的把控能力。

最终程序行数在80行左右,也是一个很合理的、GESP 3级大题的长度。

Next