C语言解pta——打印沙漏

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

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

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

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

输入格式:

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

输出格式:

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

输入样例:

19 *

输出样例: 

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

以下给出参考代码:

#include <stdio.h>
#include <math.h>

int main()
{
    int num = 0;      /* 字符个数 */
    char ch;          /* 存放字符 */
    int row = 1;      /* 表示沙漏的行数 */
    int space = 0;    /* 表示空格数 */
    int i, j;         /* 用于循环初值 */

    int Row_cal(int x);/* 定义函数,计算总个数 */

    /* 输入个数和字符 */
    scanf("%d %c", &num, &ch);

    if (num <= 0)
        return 0;    /* 如果输入的数不是正整数,则退出 */

    /* 计算row值 */    // Sn = (row+1)*(row+1) / 2 - 1
    while (Row_cal(row) <= num)
    {
        row += 2;
    }
    row -= 2;    /* 还原row值 */

    /* 输出沙漏 */
    for (i = row; abs(i) <= row; i -= 2)    /* 外层循环输出行 */
    {
        /* 输出空格数 */
        space = (row - abs(i)) / 2;
        while (space-- != 0)
        {
            printf("%c", ' ');
        }

        /* 输出每行字符数 */
        for (j = 0; j < abs(i); j++)
        {
            printf("%c", ch);
        }
        putchar('\n');          /* 换行 */

        if (1 == i)
            i = -i;
    }

    /* 输出剩余字符数 */
    printf("%d", num - Row_cal(row));
}

int Row_cal(int x)
{
    return (x + 1) * (x + 1) / 2 - 1;
}

 这道题的思路就是要计算出最大的沙漏形状,每一行先输出空格,接着输出字符。而这个沙漏有几个特点:

  1. 行数等于沙漏中字符最多的一行
  2. 行数必须为奇数
  3. 上下两对称

如果你无法理解以上代码,那么以下为代码分析: 

首先创建一系列变量,为了下面存储各值。接下来我们定义了一个函数Row_cal(x) ,目的是计算 x 行的沙漏字符总数。函数的定义放在了最后。

接下来输入数量和字符分别赋值给变量num和ch.

if (num <= 0)
        return 0; 

这段代码表示我们需要输入一个正整数。 

现在开始计算我们输入的数能输出多少行的沙漏:

while (Row_cal(row) <= num)
    {
        row += 2;
    }

int Row_cal(int x)
{
    return (x + 1) * (x + 1) / 2 - 1;
}

通过数学的数列知识,可以得到 x 行沙漏得到字符总数的公式为:Sn = (x + 1)² / 2 - 1. 在这里我们使用了一个循环,只要计算后的总数小于等于我们输入的数num,那么我们就从最小值1每次加2,直到 row 行沙漏数大于num. 我们可以发现,无论是 row 行计算后的数量是小于num还是等于 num, 退出循环后得到的 row 值都是比真正的 row 值大2. 所以接下来代码 row -= 2 还原 n 值。

拿到 row 值后,便可以开始输出沙漏了。

for (i = row; abs(i) <= row; i -= 2)    /* 外层循环输出行 */
    {
        /* 输出空格数 */
        space = (row - abs(i)) / 2;
        while (space-- != 0)
        {
            printf("%c", ' ');
        }

        /* 输出每行字符数 */
        for (j = 0; j < abs(i); j++)
        {
            printf("%c", ch);
        }
        putchar('\n');          /* 换行 */

        if (1 == i)
            i = -i;
    }

我们定义了二层循环,外层循环来输出行,内层循环输出列。我们知道,行数就等于沙漏最上(下)面一行的字符数。我们首先将 row 值赋给 i , 循环的条件为:i 的绝对值小于 row,每次循环后,i 减 2. 这样 i 的值一直等于每一行的字符数。这样可以输出对应的行数,而当 i 等于1的时候,再减2会变成 -1,沙漏打印的时候并没有-1,而是3个字符 --> 1个字符 --> 3个字符。所以当 i 为1 的时候,我们外层for循环最后使用一个 if()条件语句,当 i 等于 1 时,置换为 -1.

到此我们已经可以输出对应的行数,接下来就是每一行该输出多少字符。首先看空格,第一行不输出空格,第二行输出一个,第三行输出两个……输出一个,不输出。空格也符合回文特性。通过数学知识可以找到每一行空格数刚好与row值有对应关系:space = (row - abs(i)) / 2。所以,我们使用一个while语句在每一行上输出space个空格数,每次循环space要减1,直到为0.

现在开始打印字符,这里很简单,我们知道 i 的绝对值恰好等于每一行的字符数,那么这里直接使用for循环输出 i 个字符,此for循环结束后,换行。

最后不忘了要将剩余字符输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值