PAT (Basic Level) 1027 打印沙漏 (20 point(s))

#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 ,这就可以得到代码里面的表达式了。


说实话,写完理解之后有点迷惑🤔,好像参入的数学有点多。但是理解归理解,写代码的时候如果这样推确实有点慢。

下面这个图案循环的链接还有其他图案的代码,比如菱形等,但因为这题用不着就不管了。

参考代码         打印图案的循环理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值