PTA的每日一刷,打印沙漏
题目要求:写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印
*****
***
*
***
*****
所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。
给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。
输入格式
输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。
输出格式
首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。
解题思路
1.首先需要确定沙漏的大小,即能够用给定的符号组成的最大沙漏的大小。具体方法是通过计算沙漏每行符号的数量与总符号数之间的关系,找到能够用给定符号组成的最大沙漏大小。
2.接下来需要考虑如何打印沙漏。根据题目要求,需要按照奇数个符号递减递增的顺序打印沙漏,并保持符号中心对齐。打印时可以先打印沙漏的上半部分,再打印下半部分。对于每一行符号,需要先打印一定数量的空格,以实现符号的对齐。
3.注意要考虑沙漏大小为1的情况,此时直接打印一个符号即可。
4.可以先不考虑输出剩余符号数的问题,先实现打印沙漏的功能,确保能够正确打印出符合要求的沙漏。然后再考虑如何计算并输出剩余符号数。
画图
对于这道题,如果一开始完全没有思路,可以尝试先手工模拟一些输入数据,观察对应的输出是什么样子的,从而有助于理解题目要求和找到解题思路。例如,对于输入为17和“*”的情况,我们可以手工模拟一下如下图所示的沙漏形状:
*****
***
*
***
*****
通过手工模拟可以发现,沙漏形状上下两部分的每一行都是相同的,只是符号数量和行数的递增递减规律不同。这启示我们可以通过循环打印沙漏上下两部分来实现打印整个沙漏的功能,每次打印一行需要考虑打印空格和符号的数量以及递增递减规律。
此外,也可以先考虑如何计算出能够用给定符号组成的最大沙漏的大小,然后再考虑如何打印沙漏。具体来说,计算沙漏大小可以考虑使用数学方法,通过观察沙漏的上半部分和下半部分的
步骤
首先需要计算出能够用给定符号组成的最大沙漏的大小,具体可以使用以下步骤:
1.读入符号个数n和符号c,计算出沙漏最底部的行数rows。
2.从1开始枚举每一行的符号个数num,根据沙漏上下两部分符号数的递增递减规律,可以得到沙漏上半部分的行数和下半部分的行数分别为(i+1)和(rows-i)*2-1。
3.如果当前num个符号无法用完剩余的符号,则说明上一行的符号个数是最大沙漏的底部行数,返回即可。
接下来可以考虑如何打印沙漏。具体可以使用以下步骤:
1.使用双重循环打印沙漏的上半部分,外层循环从0到(i+1)-1,内层循环从0到num-1,每次打印num个符号并输出对应数量的空格。需要注意的是,每一行符号的数量是递减的,即第一行num个符号,最后一行1个符号。
2.使用双重循环打印沙漏的下半部分,外层循环从0到(rows-i)*2-3,内层循环从0到num-1,每次打印num个符号并输出对应数量的空格。需要注意的是,每一行符号的数量是递增的,即第一行3个符号,最后一行num个符号。
3.最后需要输出剩余的符号数量,即用给定符号组成的最大沙漏的符号数量与n的差值。注意,如果最大沙漏的底部行数为1,则剩余符号数为0。
代码实现
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int n, k = 1;
char c;
cin >> n >> c;
while (2 * k * k - 1 <= n) {
k++;
}
k--;
int left = n - (2 * k * k - 1);
for (int i = k; i >= 1; i--) {
for (int j = 0; j < k - i; j++) {
cout << " ";
}
for (int j = 0; j < 2 * i - 1; j++) {
cout << c;
}
cout << endl;
}
for (int i = 2; i <= k; i++) {
for (int j = 0; j < k - i; j++) {
cout << " ";
}
for (int j = 0; j < 2 * i - 1; j++) {
cout << c;
}
cout << endl;
}
cout << left << endl;
return 0;
}