课后作业

2023/12/1

# 第七课

# 1. 编写一个程序,提示用户输入一组数字并找到这组数字中的最大值和最小值,最后输出它们

// 输入输出示例:
// 为详细展示示例,所有的输入都添加了换行,实际编写输入打印可以不换行
// 输出
请输入这组数字包含的数字数量:
// 输入
3
// 输出
请输入 3 个数字:
// 输入
666 777 888
// 输出
最大值是: 888
最小值是: 666
  • 你的答案
#include <cstdio>
#include <iostream>
int main()
{
    int i;
    int in;
 	float max,min;
    printf("请输入这组数字包含的数字数量: ");
	std::cin >> in;
	float num[in];
	printf("请输入%d个数字: ",in);
    for(i=0;i<in;i++)
    {
     scanf("%f",&num[i]);
    }
    max=min=num[0];
    for(i=1;i<10;i++)
    {
     if(max<num[i])
     max=num[i];
     else if(min>num[i])
     min=num[i];

    }

    printf("最大为:%f\n最小为:%f\n",max,min);
    return 0;
}
  • 结果 打印错误
请输入这组数字包含的数字数量: 3
请输入3个数字: 666 777 888
最大为:888.000000
最小为:0.000000
  • 解析
    本题主要考查条件语句循环语句变量值存储以及交换

    • 题解:
      分两次输入,对应两次输出
      第一次输入对应这组数字包含的数字数量,那么设置一个变量进行存储
      我们设这个变量为n随后进行对应的第一次输出,即请输入 n 个数字
      第二次输入为对应n的个数的数字,例如:n=3即输入三个数字
      那么此时设想我们对于三个数比较大小并输出的操作,使用变量值存储以及交换来达到其效果
      设置变量初始数num,最大数maxNum,最小数minNum,即

      if (num > maxNum) {
          maxNum = num;
      }
      if (num < minNum) {
          minNum = num;
      }
      

      那么对于此题,对于需要比较的数量不确定,但已知其变量,可使用循环操作
      每次循环进行一次三数状态对比,需要注意的是maxNumminNum的初始值
      在我们使用int进行初始化时,其变量初始值为0,故此需要在第一输入后对其变量初始值进行赋值
      否则将会在比较时发生错误,那么此题题解结束,详细代码请参考参考答案

    • 你的错误
      首先对于变量的定义,题目无明显要求且输出示例为整型下,无需使用float类型
      即使使用了float类型,请最后输出与示例一致,即printf使用错误
      在计算中使用了数组,在未学习数组前,不建议使用,且当最后输出错误时应思考更改方法
      课后习题不是考试,错了也无所谓,为的是巩固课上知识而准备的
      在此题环境下,使用单个变量追踪最大值和最小值通常在这种情况下更有效率,同时减少了对动态内存分配的需求
      此外,在C++中,数组的长度必须是编译时常量
      使用变量 in 定义数组长度是不合法的,因为 in 的值只在运行时确定
      正确的做法是使用动态内存分配或使用 std::vector,在这里我将使用动态数组作为演示

      int in;
      printf("请输入这组数字包含的数字数量: ");
      std::cin >> in;
      float* num = new float[in];
      

      数组越界问题,在下方循环中,程序使用了常量 10 作为数组的上限
      这是一个硬编码的数字,而应该使用用户输入的数字 in 作为上限
      输入方式混合,可能会导致输入缓冲区的问题,最好只使用一种输入方式

  • 参考答案

#include <iostream>
using namespace std;
int main() {
    int n;
    cout << "请输入数字的数量: ";
    cin >> n;
    // 此条件语句无示例输出,可省略
    if (n <= 0) {
        cout << "无效的数字数量,程序结束。" << endl;
        return 0;
    }
    cout << "请输入 " << n << " 个数字:" << endl;
    int num;
    int maxNum, minNum;
    for (int i = 0; i < n; ++i) {
        cin >> num;
        if (i == 0) {
            // 初次循环,将 num 同时赋值给 maxNum 和 minNum
            maxNum = num;
            minNum = num;
        } else {
            if (num > maxNum) {
                maxNum = num;
            }
            if (num < minNum) {
                minNum = num;
            }
        }
    }
    cout << "最大值是: " << maxNum << endl;
    cout << "最小值是: " << minNum << endl;
    return 0;
}

# 2. 使用嵌套循环生成以下图形(空格用于对齐)

// 输出示例:
    *
   ***
  *****
 *******
*********
  • 你的答案
#include<stdio.h>

int main()

{

    int i,j,k;

    for(i=0;i<=4;i++) //这个是控制要输出几行“*”

    {

        for(j=0;j<=3-i;j++) //这个控制每行输出几个“空格”,跟i的关系来决定的

             printf(" ");

        for(k=0;k<=2*i;k++) //这个控制输出空格之后,需要输出几个“ * ”号,

             printf("*");

     printf("\n"); //在外层循环内部,内层循环的外部,意思内层循环结束一次后,换行。

    }

}

  • 结果 打印正确

  • 解析 本题考查嵌套循环语句使用

  • 参考答案 无,答案程序输出完全正解

# 3. 创建一个简单的猜数字游戏,程序随机生成一个1100之间的整数,然后让用户通过输入猜测这个数字,直到猜对为止。在每次猜测后,程序应该告诉用户猜得太高或太低,最后猜测成功后输出猜测次数

生成随机数示例
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
    // 设置随机种子,使用当前时间作为种子
    srand(static_cast<unsigned int>(time(0)));

    // 生成随机数
    int randomNumber = rand();
    cout << "随机数: " << randomNumber << endl;

    // 生成 1 到 100 之间的随机数
    int secretNumber = rand() % 100 + 1;
    cout << "1 到 100 之间的随机数: " << secretNumber << endl;

    return 0;
}
// 输入输出示例:
// 为详细展示示例,所有的输入都添加了换行,实际编写输入打印可以不换行
// 输出
欢迎参加猜数字游戏!
猜一个1100之间的数字:
// 输入
50
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
40
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
30
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
20
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
10
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
8
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
5
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
6
// 输出
猜得太高了,请再试一次。
猜一个1100之间的数字:
// 输入
7
// 输出
恭喜你猜对了!用了 9 次尝试。
  • 你的答案
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstdio>
using namespace std;
int main() {
    // 设置随机种子,使用当前时间作为种子
    srand(static_cast<unsigned int>(time(0)));
	// 生成 1 到 100 之间的随机数
    int secretNumber = rand() % 100 + 1;
    int ynb;
    int t;
    cout <<"欢迎来到猜数字游戏!\n";
    cout <<"猜一个1到100之间的数字: ";
	cin >> ynb;
	while (ynb == secretNumber){
		if (ynb > secretNumber){
			cout <<"猜得太高了,请再试一次。\n" ;
			cout <<"猜一个1到100之间的数字: ";
			cin >>ynb;
			t += 1;
		}else{
			cout <<"猜得太低了,请再试一次。\n" ;
			cout <<"猜一个1到100之间的数字: ";
			cin >>ynb;
			t += 1;
		}
	}
	printf("恭喜你猜对了!用了%d次尝试。",t);
    return 0;
}
  • 结果 打印错误
欢迎来到猜数字游戏!
猜一个1100之间的数字: 50
恭喜你猜对了!用了0次��试。
  • 解析 本地考查循环语句条件判断语句的使用

    • 题解:
      首先分析输入输出,对于此题多输入多输出,但除初始输出提示后,每次输入对应一个输出
      除初始输出外,输入输出可看作一个单元模块的处理,对于此题处理可以使用if条件语句来处理,多模块的打印则使用for循环语句来处理
      随机数方案已知,将其作为判断参数即可
      对于此题难点即为在用户未输入正确数字时需不停循环同时在客户答对后终止循环并跳出
      首先我们分析for循环的循环条件,即其中央条件表达式成立
      则我们可以设置true为其中央条件表达式来保证其循环不停歇
      而后我们需要客户答对后终止循环并跳出,则可以在判断答对后添加break来结束
      此题题解结束,详细代码请参考参考答案

    • 你的错误
      while (ynb == secretNumber) 这个条件会导致循环只在用户猜对的情况下执行
      但我们希望循环在用户没有猜对的情况下执行。应该使用 while (ynb != secretNumber)
      在每次循环迭代中,应该根据用户的猜测判断是太高还是太低,然后提供相应的提示
      因此,应该使用 if-else 来判断,而不是 while
      初始时 t 没有被赋初值,所以在 printf 语句中输出 t 的值时可能会出现不确定的结果
      应该将 int t = 0; 放在程序开始的地方,给 t 一个初始值
      课后作业的目的是巩固课上的练习,建议使用已学习知识,如超纲比如第五课的情况应与老师即时联系 ✨

  • 参考答案

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
    // 设置随机种子
    srand(static_cast<unsigned int>(time(0)));
    // 生成1到100之间的随机数
    int secretNumber = rand() % 100 + 1;
    int guess;
    cout << "欢迎参加猜数字游戏!" << endl;
    for (int attempts = 1;true; ++attempts) {
        cout << "猜一个1到100之间的数字: ";
        cin >> guess;
        if (guess == secretNumber) {
            cout << "恭喜你猜对了!用了 " << attempts << " 次尝试。" << endl;
            break;
        } else if (guess < secretNumber) {
            cout << "猜得太低了,请再试一次。" << endl;
        } else {
            cout << "猜得太高了,请再试一次。" << endl;
        }
    }
    return 0;
}

# 4. 一只青蛙从地面开始起跳,他一次可以跳1级台阶,也可以一次跳2级台阶,...,也可以一次跳n级台阶。求这只青蛙跳上一个n级台阶总共有多少办法

小提示:对于抽象概念的习题,一时没有思路,我们可以按照规则在纸上先尝试画出部分再查找其规律,最后总结为公式。

// 输入输出示例:
// 为详细展示示例,所有的输入都添加了换行,实际编写输入打印可以不换行

// 示例 1:
// 输出
请输入台阶的级数 n:
// 输入
3
// 输出
青蛙跳上 3 级台阶的方法总数为: 4

// 示例 2:
// 输出
请输入台阶的级数 n:
// 输入
6
// 输出
青蛙跳上 6 级台阶的方法总数为: 32
  • 你的答案
#include<stdio.h>
int count = 0;//创建全局变量来统计个跳法个数
void frog(int n)
{
	if (n == 0)//当台阶数为0是跳法个数加1
		count++;
	else if (n < 0);
	else
	{
		frog(n - 1);
		frog(n - 2);
	}

}
int main()
{
	int n;
	printf("请输入台阶个数:");
	scanf("%d", &n);
	frog(n);
	printf("一共有%d种跳法\n", count+1);
	return 0;
}

  • 结果 打印错误
请输入台阶个数:6
一共有14种跳法
  • 解析 此题考查菲波那契数列变形,即考查循环语句变量值存储以及交换

    • 题解: 对于抽象概念的习题,一时没有思路,我们可以按照规则在纸上先尝试画出部分再查找其规律,最后总结为公式
      下面是每一级台阶青蛙的选择情况的示例:
      1级:跳或者不跳(20 = 1 种选择)
      2级:跳1级、跳2级或者不跳(21 = 2 种选择)
      3级:跳1级、跳2级、跳3级或者不跳(22 = 4 种选择)
      ...
      n级:跳1级、跳2级、...、跳n级或者不跳(2(n-1)种选择)
      使用一个变量 ways 来表示青蛙跳上台阶的方法总数,初始化为1,因为当台阶级数为0时,青蛙已经在台阶上了
      使用for循环遍历1n-1的每一级台阶,每遍历一级,将 ways 乘以2,表示青蛙在这一级有两种选择:跳或者不跳
      输出最终的 ways 值,表示青蛙跳上n级台阶的方法总数
      此题解题思路有很多,比如如上的数学归纳法,亦或是利用动态规划的思想
      每个台阶都可以直接跳上去,也可以从前边每个台阶跳上来
      所以每个台阶的可能性都是把其前边所有台阶的可能性加起来,再加1(就是直接跳上去的可能性)
      1. 假设青蛙跳上 n 级台阶的方法总数为 f(n)
      2. 我们考虑在跳到第 n 级台阶时,青蛙有两个选择:直接从第 n-1 级跳上来,或者从第 n-2 级、n-3 级,一直跳到第 1 级再跳上来。最后,青蛙还有一种选择就是直接跳上第 n
        • f(n-1) 表示青蛙从第 n-1 级跳上来的方法总数
        • f(n-2) 表示青蛙从第 n-2 级跳上来的方法总数
        • ...
        • f(1) 表示青蛙从第 1 级跳上来的方法总数
        • 1 表示青蛙直接跳上第 n 级的方法总数
      3. 将上述所有可能性加起来,即 f(n) = f(n-1) + f(n-2) + ... + f(1) + 1
      4. 进一步,我们注意到 f(n-1) = f(n-2) + f(n-3) + ... + f(1) + 1
      5. 将第 3 步的等式减去第 4 步的等式,得到 f(n) - f(n-1) = f(n-1)
      6. 化简得到 f(n) = 2f(n-1)
        我们可以通过逐步展开这个递推关系来得到最终的结果:
        f(n) = 2f(n-1)
        = 2 * 2f(n-2)
        = 22 * f(n-2)
        = 22 * 2f(n-3)
        = 23 * f(n-3)
        ...
        = 2(n-1) * f(1)
        因为 f(1) = 1 (青蛙一次可以跳 1 级台阶的情况),所以最终的结果是 f(n) =2(n-1)
    • 你的错误 在函数递归的 else if 分支中,有一个分号 ;,导致这个分支为空语句,即 else if (n < 0);
      这会导致在 n 小于 0 时不执行任何操作,从而出现错误的计数结果
      当然错误并不在此,而是没有完成递归应有的计算,不建议使用未学知识点
      课后作业的目的是巩固课上的练习,建议使用已学习知识,如超纲比如第五课的情况应与老师即时联系 ✨
      错误的原因是因为此代码只考虑到1级以及2级的情况,且其写法错误,同步多次调用函数将重复大量运算,对最外围全局作用域变量产生错误影响
  • 参考答案

#include <iostream>
using namespace std;
int main() {
    int n;
    cout << "请输入台阶的级数 n: ";
    cin >> n;
    // 初始化结果为1,因为当n为0时,青蛙已经在台阶上了
    int ways = 1;
    for (int i = 1; i < n; ++i) {
        ways *= 2;
    }
    cout << "青蛙跳上 " << n << " 级台阶的方法总数为: " << ways << endl;
    return 0;
}

# 5. 我们可以用 2x1(2x1 代表的是长 X 宽,即长为 2,宽为 1,此题无需考虑单位) 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n2x1 的小矩形无重叠地覆盖一个 2xn 的大矩形,总共有多少种方法

小提示:对于抽象概念的习题,一时没有思路,我们可以按照规则在纸上先尝试画出部分再查找其规律,最后总结为公式。

// 输入输出示例:
// 为详细展示示例,所有的输入都添加了换行,实际编写输入打印可以不换行

// 示例 1:
// 输出
请输入大矩形的宽度 n:
// 输入
2
// 输出
2x1 的小矩形覆盖 2x2 的大矩形的方法总数为: 2

// 示例 2:
// 输出
请输入大矩形的宽度 n:
// 输入
4
// 输出
2x1 的小矩形覆盖 2x4 的大矩形的方法总数为: 5
  • 你的答案
#include <cstdio>
#include <iostream>
using namespace std;
int rectCover(int number) {
    //也是斐波那契数列,n==1时1种,n==2时2种,n==3时3种...f(n)=f(n-1)+f(n-2)
        if(number==1)
            return 1;
        else if(number==2)
            return 2;

        int first=1;
        int second=2;
        int sum=0;

        while(number>2)
        {
            sum=first+second;
            first=second;
            second=sum;

            number--;
        }

        return sum;
}
int main(){
	int so ;
	printf("请输入大矩形的宽度 n:");
	cin >> so;
	printf("2x1 的小矩形覆盖 2x2 的大矩形的方法总数为: %d",rectCover(so));
	return 0;
}
  • 结果 打印正确

  • 解析 此题考查菲波那契数列变形,主要考查条件语句循环语句变量值存储以及交换
    课后作业的目的是巩固课上的练习,建议使用已学习知识,如超纲比如第五课的情况应与老师即时联系 ✨
    这是一个经典的递归问题,可以使用动态规划来解决。设 f(n) 表示覆盖 2xn 的大矩形的方法总数
    考虑最后一个小矩形的摆放方式,有两种情况:

    1. 最后一个小矩形竖着放:这种情况下,前面的部分就是 2x(n-1) 的大矩形,共有 f(n-1) 种摆放方式
    2. 最后一个小矩形横着放:这种情况下,最后两列都是横着放的,前面的部分就是 2x(n-2) 的大矩形,共有 f(n-2) 种摆放方式
      因此,总的摆放方式为 f(n) = f(n-1) + f(n-2)
  • 参考答案

#include <iostream>
using namespace std;
int main() {
    int n;
    cout << "请输入大矩形的宽度 n: ";
    cin >> n;
    if (n <= 0) {
        cout << "无效的宽度。程序结束。" << endl;
        return 1;
    }
    // 两个变量 fn_2 和 fn_1 分别表示 f(n-2) 和 f(n-1),通过循环计算得到 f(n) 的值
    // 初始化 f(n-2) 和 f(n-1)
    int fn_2 = 1; // 初始时 f(0) = 1
    int fn_1 = 1; // 初始时 f(1) = 1
    // 计算 f(n)
    for (int i = 2; i <= n; ++i) {
        int fn = fn_1 + fn_2;
        fn_2 = fn_1;
        fn_1 = fn;
    }
    cout << "覆盖 2x" << n << " 的大矩形的方法总数为: " << fn_1 << endl;
    return 0;
}
上次更新: 2024-10-19 10:01:51