#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n, line = 0;
char c;
cin >> n >> c;
// 判断多一行时是否符号数大于n 否则递增line
while (n >= 2 * pow(line + 1, 2) - 1) {
line++;
}
// 从第一行开始
for (int i = 1; i < 2 * line; ++i) {
// 空格
for (int j = 1; j <= line - abs(i - line) - 1; ++j)
cout << " ";
// 字符
for (int j = 1; j <= 2 * abs(i - line) + 1; ++j)
cout << c;
cout << endl;
}
// 给定n - 沙漏图案数量 = 剩余符号数
cout << n - 2 * pow(line, 2) + 1;
}
写题时可以按题目条件先给出自己的想法,并给出实现方法和函数,而不需要具体实现。如果发现其中有难以描述的地方,或者无法想出解决方案,那就可以把不懂的地方与答案比较,学习别人的思路。
这样避免直接冲进去解题,但却卡在某一两个点上,或不具备高效的实现方法,导致浪费时间。
while (n >= 2 * pow(line + 1, 2) - 1) {
line++;
}
line 从0开始,while先判断三角形下一行是否超过给定n的数量,如果不超过则令 line++ 递增。
而计算沙漏数量的表达式 2 * pow(line + 1, 2) - 1 即计算两个三角形的图案数量,再减去1。因为沙漏只有一个顶点,两个三角形会多一个顶点。
至于为什么会用pow来算一个三角形的图案数量。首先 line + 1 是底数,2是指数。而一个三角形的图案数量我们可以先从 1 3 5 7 ……看出规律。可以看到每一行是奇数,所以用前n项和的公式可以得到 (a1 + an) * n / 2 = ((1 + 2n - 1) * n / 2) / 2, 整理可得 n ^ 2 也就是这个pow的含义,而line + 1 代入底数 n 即可。
i < 2 * line
2 * abs(line - i) + 1
line - abs(line - i) - 1
理解下参考代码里面这两个神奇的绝对值代码,原代码中,这两代码分别用于打印字符(上)和空格(下)。
line是一个正或倒三角形的行数(比如下图就是line = 3),i 是循环变量从 1 - line 递增遍历。所以这里 line 是固定的,只有 i 变化。比如3行的三角形,i 从1开始可以看作当前打印的三角形行数,那么 1 - 3 = -2,取绝对值之后 2。因为字符数是奇数,乘以2再加1后,就可以得到第一行三角形需要打印的字符数。
同理下一行 i++ = 2,| 2 - 3 | = 1 乘2加1后得3。以此类推到第三行。可以看到,打印倒三角形的时候,随着 i++ 字符数递减。
而到正三角形,因为 i 递增 line 不变,且这时候 i > line了,故 i++ 而字符数递增。就有 3、5个字符数的结果。
如果以函数来表示,首先我们观察到沙漏的变化呈现先递减后递增,再者我们发现字符数呈现为奇数。这样我们以line为自变量,字符数为因变量,就可以得到上面这个代码表达式,函数式可以得到(如下图),就可以得到我们整个沙漏的每行图案个数。
而这就是这个代码的意义。其中很重要的是绝对值,因为绝对值把因变量(字符数)的范围限定在非负范围。如果不加其他参数长下面这样,这可以匹配我们最开始对于沙漏的观察,先递减后递增。
而除了这个条件以外,我们还需要满足因变量的结果是奇数,且我们在第三行的时候需要得到结果为1。为了得到奇数,因为我们 i 从 1 开始,所以就有 2 * | i | + 1。
可以看到现在 0 的时候为 1,但我们需要第三行自变量 i = 3 时 因变量为 1,所以做一个向右平移的处理,即 i - 3,有 2 * | i - 3 | + 1,这就得到我们需要的关系式了。
想起来,由于绝对值的缘故,内部写成 i - line 或者 line - i 都可以。但“左加右减”这个口诀移动自变量前面 i 是带正号的, 所以 i - line 是比较标准的写法。
从得到字符数的函数思路,同理可以得到空格的函数式。空格的单调性与字符相反,先加后减,而且空格是1 :1的关系,初始点 i = 1 空格数为 0。
因为单调性相反给绝对值前面加一个负号,因为1:1关系,所以因变量前面不需要加其他的常数。
根据图案给我们的数据,第一行的时候为0空格,第三行的时候为2空格,第三行的时候是最大值,所以我们把顶点右移三格,并且上移2可以得到函数式。
代入变量,并且将上面的2转化成line表示,就可以得到 - | i - line | + line - 1 整理有 line - | i - line | - 1 ,这就可以得到代码里面的表达式了。
说实话,写完理解之后有点迷惑🤔,好像参入的数学有点多。但是理解归理解,写代码的时候如果这样推确实有点慢。
下面这个图案循环的链接还有其他图案的代码,比如菱形等,但因为这题用不着就不管了。