题目
分析
本题难度正如题目所称,是标准的GESP 3级难度,涉及了循环、分支、数组的综合应用,同时还有很多坑需要注意避免。
首先,需要分析清楚题意并在脑海中/笔记上反应出相应的核心算法和过程。本题的核心算法和过程是什么呢?
获得一个数字的个位数很简单: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 == m。x == 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;
}
完整代码见下图。
答案

思考
需要提一句的是,我们这里直接用n = x * pow(10, len) + remain;计算新的数字,但pow是个浮点数运算函数,可能存在一些误差。不过,在本题中,这个问题问题并未出现。更好的方法,是直接用个小循环或整数幂函数计算\(10^{len}\)(例如累乘或预计算表)。
如前所述,本题是典型的 GESP 3级编程题,涉及循环、分支与数组等基本语法,同时也考察对边界与实现细节的把控能力。
最终程序行数在80行左右,也是一个很合理的、GESP 3级大题的长度。
