经典之打印沙漏-数学分析-C/C++
打印沙漏
打印沙漏的这道问题可以说是经典之极,从我学程序设计、到学数据结构、以及后来参加各种程设赛,这道题几乎都必须经历。这道题真正涉及到的代码语法并不难,难的是如何根据给定的一个数字确定能实现的最大沙漏、沙漏行数、沙漏每行的符号数和空格数。初做这道题时想了很久,最后也是暴力求解——“试”出来最大沙漏数、沙漏行数等。但是毕竟学了多年数学,一直觉得一定能有一个数学关系式把最大沙漏、沙漏行数、沙漏每行的符号数和空格数这些变量都给联系起来。
最近在PTA刷题的时候又遇到了这道题,静下心来又分析了一下题目,最终也是终于找到了我要的关系式,因此写这篇文章分享出来。废话不多说,咱们开始。首先老规矩,原题内容如下:
1 题目内容
本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“ * ”,要求按下列格式打印
*****
***
*
***
*****
所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。
给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。
输入格式:
输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。
输出格式:
首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。
输入样例:
19 *
输出样例:
*****
***
*
***
*****
2
2 题目分析
题目的输入只有两个变量:符号数量、相应的符号,剩下的内容全靠我们自己分析推导。为了方便起见,我分别用all
、c
两个变量名来表示两个变量。对于这两个变量,第二个变量c
只是用来告诉我们输出什么符号,格式的控制完全依靠第一个变量all
。想要正确的输出沙漏形状,我们得通过符号数量确定以下内容:
- 这些数量的符号所能实现的最大沙漏的符号数量
max
(核心问题1,基本目标) - 实现最大沙漏后剩余的符号数量
surplus
- 所能实现的最大沙漏的行数
N
和每行的符号数cha
(核心问题2,控制输出格式) - 沙漏每行的空格数
spa
(核心问题3,居中控制)
有了数据和目标,接下来的任务就是实现这些目标。前面也提到过,上面这些变量我们都可以通过暴力求解法枚举求得。但这毕竟不符合我们对代码高效、简洁的追求。而且,这些变量之间明显存在着数学关系,找到这个数学关系并利用其求解,才更有工科的数学乐趣~下面,我们就利用数学思想,来找出这一关系。
3 数学归纳
为了叙述方便,后面我们的沙漏符号都使用*
来表示。首先,我们先来看看对于题目要求沙漏形状的行数和符号数。
3.1 沙漏形状的行数和符号数
对于题目要求的沙漏形状,最小的情况下即为1个符号的情况,即:
*
此时我们有:max = 1
、N = 1
。用n
表示我们的第n个沙漏,最小的沙漏即为n = 1
,即:
n | N | max |
---|---|---|
1 | 1 | 1 |
对于第二个沙漏的情况(n = 2
),即为:
***
*
***
此时我们有:max = 7
、N = 3
。通过观察两个沙漏的变化我们易知,第二个沙漏是在第一个沙漏的最顶端和最底端分别加了一行,增加的这两行的符号数 = 原顶端(底端)的符号数 + 2,所以我们有:
n | N | max |
---|---|---|
1 | 1 | 1 |
2 | 3 | 7 = 1 + 2 * 3 |
有了前面两组数据,其实第三个沙漏的情况就很好分析了。对于第二个沙漏的情况(n = 3
),即为:
*****
***
*
***
*****
此时我们有:max = 17 = 1 + 2 * 3 + 2 * 5、N = 5 = 1+2+2,即:
n | N | max |
---|---|---|
1 | 1 | 1 |
2 | 3 | 7 = 1 + 2 * 3 |
3 | 5 | 17 = 1 + 2 * 3 + 2 * 5 |
通过观察,我们已经很容易得知,对于n = n
时,我们有 N = 2n-1, max = 1 + 2 * 3 + …… + 2 * (2n - 1) 。N
已经可以表示为一个对任意n
都成立含n
的表达式了,而max
目前的表达式对n= 1
不成立,因此结果还不是很显然。此时,我们也可以到此为止,在实现代码时区分n = 1
和n > 1
的情况,但我依然认为找到一个更合适的表达式会更加方便。
因此我们再对max
分析,进一步会发现:当n = 1
时,max = 1 = 2 * 1 - 1;当n = 2
时,max = 7 = 2 * 1 + 2 * 3 - 1……由此我们有,当n = n
时,max = 2 * 1 + 2 * 3 + …… + 2 * (2n - 1) - 1 = 2 * (1 + 3 + …… +(2n - 1)) - 1。这样一来,结果就比较显而易见了。1 + 3 + …… +(2n - 1) 其实就是一个等差数列的前n项和的问题,不难求出其结果为n2,所以 max = 2n2 - 1。
至此,我们通过一个表示第n种情况下沙漏形状的变量n
,建立了max
与N
关于n
的表达式。对于指定的某个沙漏形状,n
是没有必要存在的,但是在后续的分析中,引入n
会给我们的分析和代码实现带来便利,因此我们保留n
。