L1-002 打印沙漏 (20 分)

本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

*****
 ***
  *
 ***
*****

所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。

给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。

输入格式:

输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。

输出格式:

首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。

输入样例:

19 *

输出样例:

*****
 ***
  *
 ***
*****
2

 


解题思路:解法一

记输入值为: n  ch    (即样例中n=19,ch='*')此题可以分为两个部分进行:

①根据被打印符号总数n,求出沙漏上三角部分行数h,则总行数为 (2h-1) 。

②打印的上半行数为h的沙漏

③计算余下未被打印的符号remain

①n转h:解法一

先按余下符号数remain=0时的情况分析n和r。记f(r)为r行沙漏所需符号数:

则f(1)=1= 1*2 -1

f(2)=3+1+3= (1+3)*2-1

f(3)=5+3+1+3+5 = (1+3+5)*2-1

.....以此类推,用等差数列求和公式可得:

python怎么用符号绘制沙漏 python打印沙漏图形_PAT

 

python怎么用符号绘制沙漏 python打印沙漏图形_离散函数_02

   设h=g(n),则 

python怎么用符号绘制沙漏 python打印沙漏图形_字符串处理_03

=>   

python怎么用符号绘制沙漏 python打印沙漏图形_python怎么用符号绘制沙漏_04

=>   

python怎么用符号绘制沙漏 python打印沙漏图形_离散函数_05

=> 

python怎么用符号绘制沙漏 python打印沙漏图形_PAT_06

这个化简其实只需要高中数学良好就可以完成,计算熟练者1分钟都不用。

对于数学功底较弱的同学,也可以根据式用迭代的方法(见解法二)根据n求出对应的h。只是程序执行的时间稍微长一点,代码稍微多一点。

②沙漏:解法一(函数法)

之所以不以沙漏的总行数r为输入,而要定义上半三角行数h,是有原因的。

要打印这个沙漏无非就是确定要打印的每一行的空格数s ,和ch字符数 c。

注意不能用Python自动居中对齐的打印方法,因为每一行最后一个字符必须是ch,不能用空格补齐。

定义每一行空格数和ch字符数关于h的数列函数分别为 S(h)和 C(h)

则 C(1)=[1],C(2)=[3,1,3],C(3)=[5,3,1,3,5],C(4)=[7,5,3,1,3,5,7]

     S(1)=[0],S(2)=[0,1,0],S(3)=[0,1,2,1,0],S(4)=[0,1,2,3,2,1,0]

首先S(h)的规律比较明显

本文定义:数与数列的运算或函数,是对数列中的每个元素都采取同样运算。

记 J(h+1) = h -S(h+1)  (提示:就是将S(h+1)中的每个元素都用h去减)

直接上图,以h=6为例:

python怎么用符号绘制沙漏 python打印沙漏图形_离散函数_07

不难得出  C(h)= 2J(h) +1

则 S(h+1) = h - J(h+1)

只要在遍历中整出 [5,4,3,2,1,0,1,2,3,4,5]的效果即可,那不就是[-5,-4,-3,-2,-1,0,1,2,3,4,5]取绝对值吗?

由此可以得到打印漏斗的函数,Python代码如下:

def sand_clock(ch,h):
    for n in range(1-h,h):  #如h=5时表示 [-4,-3,-2,-1,0,1,2,3,4]
        print(' '*(h-1-abs(n))+ch*(2*abs(n)+1))	#Python里 字符串*k,其中k为整数,表示该字符串重复k次

③剩余字符:

解法一:配合①中的函数解法,由 h=g(n)得到h,再由 f(h)=n0得到实际使用的符号数量n0,remain = n - n0。

解法二:笨方法,在打印沙漏的循环中累加统计已用字符数量,最后用n减去即可。

解法一代码:

Python版本:(就不写成函数了)

n,ch=input().split(' ')
n=int(n)	#ch的总数量
h=int(((n+1)/2)**0.5)		# h*2-1 为行数,也是首行的ch数量
for i in map(abs,range(1-h,h)):    #map(abs,list)表示对list的每个元素都取绝对值
    print(' '*(h-1 -i)+ch*(2*i+1))     # h-i 个空格,接(2*i+1)个ch
print( n - (2*h**2 -1) )	#ch实际数量为: 2 * h^2 -1

C语言版:

#include<stdio.h>
#include<math.h>
void print(char ch, int count) {
	while (count-- > 0)putchar(ch);
}	//print(ch,c):打印ch字符c遍。
int main() {
	int n, ch, h,i;
	scanf("%d %c", &n, &ch);
	h = (int)sqrt((n + 1) / 2); //h=g(n)
	for (i = 1-h; i < h; i++){
		int j = abs(i);
		print(' ', h - 1 - j);	//空格
		print(ch, 2 * j + 1);	//ch
		putchar('\n');	//换行
	}
	printf("%d", n - (2 * h*h - 1));
	return 0;
}

解法二:笨办法

由于本人是直接按解法一秒杀此题的,只是怕同学不理解,就写个解法二:

解法二就不解释这么多了,直接上代码,看注释:

#include <stdio.h>
void print(char ch, int count) {
	while (count-- > 0)putchar(ch);
}	//print(ch,c):打印ch字符c遍。
void printLine(char ch, int j, int width) {
	print(' ', j);	//j为空格数量
	print(ch, width - 2*j);	//width一行总宽度  - 两侧的空格数量2j
	putchar('\n');	//换行(右侧的空格不需要也不允许真正地输出)
}	//打印沙漏专用,ch为待打印字符,h为上三角层数
int main(){
	int n, n0 = 1;	//n ch即为题目输入。n0是沙漏实际打印字符个数
	char ch;	//样例中ch=='*'
	int h = 1;	//沙漏上三角层数
	scanf("%d %c", &n, &ch);
	while (1){	//迭代法试出
		int temp = n0 + 2 * (2 * h + 1);	//新增加首末两行的ch数量
		if (temp <= n) {		//没有超出n
			n0 = temp;	h++;
		}else break;	//否则超出则跳出
	}
	int j, row = 2 * h - 1;	//j为空格数量,row为总行数==首末行ch数量
	for (j = 0; j<h; j++) {		//打印上半部分(含中间)
		printLine(ch, j, row);
	}
	for (j = h-2; j >= 0; j--){	//打印下半部分(不含中间)
		printLine(ch, j, row);
	}
	printf("%d", n - n0);
	return 0;
}

欢迎各位交流探讨,发表合理评论或提意见。