一、题目
本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印:
∗ ∗ ∗ ∗ ∗ ***** ∗∗∗∗∗
∗ ∗ ∗ *** ∗∗∗
∗ * ∗
∗ ∗ ∗ *** ∗∗∗
∗ ∗ ∗ ∗ ∗ ***** ∗∗∗∗∗
输入格式:
输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。
输出格式:
首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。
输入样例:
19 *
输出样例:
∗ ∗ ∗ ∗ ∗ ***** ∗∗∗∗∗
∗ ∗ ∗ *** ∗∗∗
∗ * ∗
∗ ∗ ∗ *** ∗∗∗
∗ ∗ ∗ ∗ ∗ ***** ∗∗∗∗∗
2
二、思路和注意事项
打印沙漏是关于等差数列的题。该等差数列是一个首项 a 1 = 1 a_1=1 a1=1,公差为 d = 2 d=2 d=2,的等差数列。第 n n n项为: a n = 1 + ( n − 1 ) × 2 = 2 n − 1 a_n=1+(n-1)\times2=2n-1 an=1+(n−1)×2=2n−1.
先考虑上半部分:
- 沙漏的第1行是是等差数列的第n项,相邻的两行之间相差d,最后一行为 1 1 1
- 要计算刚好构成沙漏的个数,我们只需要对等差数列求和,再减去1,因为上半部分和下半部分重复首项1.
- 由于上半部分总个数为 ( 1 + 2 n − 1 ) × n 2 , 所 以 总 数 S = ( 1 + 2 n − 1 ) × n 2 × 2 − 1 = n 2 − 1 \frac {(1+2n-1)\times n} 2, 所以总数S=\frac {(1+2n-1)\times n} 2\times2-1=n^{2}-1 2(1+2n−1)×n,所以总数S=2(1+2n−1)×n×2−1=n2−1
所以我们求上半部分的行数 n n n则为 n + 1 2 \sqrt{\frac{n+1}2} 2n+1。
如果n刚好能构成一个沙漏,则 n + 1 2 \frac {n+1}2 2n+1是一个完全平方数。
所以该函数的意思是,传入的 N自减,当N刚好能够构成一个沙漏时,返回构成该沙漏的行数。
下半部分:
我们把上半部分的最后一行当做下半部分的第二行,所以我们的循环输出从i=2开始,空格的输出是上半部分的相反循环:
先计算上半部分一共有多少行。例如输入19时,返回值为3
int getrow(int i)
{
int row=1;
int cmp=sqrt((i+1)/2);
while(i--)
{
if(row==cmp)
{
row=cmp;
break; //没有break 会让i减到零
}else row++;
}
return row;
}
三、AC代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int getrow(int i);
int main()
{
int N;
cin>>N;
char a;
cin>>a;
int row=getrow(N);
int cmp=2*row*row-1;
//上半部分
for(int i=row;i>=1;i--) //控制行输出,沙漏上半部分上大下小,故从第n项输出到第1项.
{
for(int k=0;k<row-i;k++) //第一行空格为0,第一行空格为1,..第n行空格为n-1
{
printf(" ");
}
//输出空格完以后输出字符
for(int j=2*i-1;j>=1;j--) //控制列,第n项个数为2*n-1
{
if(j==1) printf("%c\n",a);
else printf("%c",a);
}
}
//下半部分
for(int i=2;i<=row;i++) //把沙漏上半部分最后一行当做下半部分的第一行,所以从2开始循环
{
for(int k=row-i;k>=1;k--)
{
printf(" ");
}
for(int j=1;j<=2*i-1;j++)
{
if(j<2*i-1) printf("%c",a);
else printf("%c\n",a);
}
}
printf("%d\n",N-cmp); //输出给定的数字N和刚好构成沙漏的数量之差
}
int getrow(int i)
{
int row=1;
int cmp=sqrt((i+1)/2);
while(i--)
{
if(row==cmp)
{
row=cmp;
break; //没有Break 会让temp减到零
}else row++;
}
return row;
}