C语言: 我做过的练习题
第一题:喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以喝多少汽水(编程实现)
int main()
{
int bottle_price = 1;
int price = 20;
int bottle_num = price / bottle_price;
int num = price / bottle_price;
while (bottle_num > 1)
{
bottle_num -= 2;
num += 1;
bottle_num += 1;
}
printf("%d", num);
}
你编写的代码在逻辑上是正确的,能够正确计算出20元可以喝到的汽水总数。以下是具体分析:
代码逻辑验证
问题的核心逻辑是:初始用20元买20瓶汽水(喝完得20个空瓶),之后每2个空瓶换1瓶新汽水(喝完又产生1个空瓶),直到剩余空瓶不足2个为止。
你的代码通过循环模拟了这一过程:
初始状态:bottle_num = 20(20元买20瓶),num = 20(已喝20瓶)。
循环条件:while (bottle_num > 1)(只要空瓶数大于1,就能继续换汽水)。
循环体:
bottle_num -= 2:用掉2个空瓶换1瓶。
num += 1:喝到1瓶新汽水。
bottle_num += 1:喝完新汽水后,新增1个空瓶(原2个空瓶被消耗,剩余1个新空瓶)。
关键测试用例验证
当输入为20元时:最终输出39瓶(正确结果)。
当输入为2元时:输出3瓶(2元买2瓶→换1瓶→共3瓶,正确)。
当输入为3元时:输出5瓶(3元买3瓶→换1瓶→再换1瓶→共5瓶,正确)。
结论
代码逻辑正确,能够准确计算出20元可以喝到的汽水总数。没有问题。
—————————————————————————————————
题目二:打印图案
打印一个菱形,
int main()
{
int row = 7;
int col = 13;
int i, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (j >= 6 - i && j <= 6 + i)
printf("*");
else {
printf(" ");
}
}
printf("\n");
}
for (i = row - 1; i > 0; i--)
{
for (j = 0; j < col; j++)
{
if (j >= 6 - i + 1 && j <= 6 + i -1)
printf("*");
else {
printf(" ");
}
}
printf("\n");
}
}
关键步骤分析
- 上半部分(正金字塔)
外层循环i从0到6(共7行),控制金字塔的层数。
内层循环j从0到12(共13列),通过条件j >= 6 - i && j <= 6 + i确定的范围:
当i=0时,j范围为6(仅中间1个)。
当i=1时,j范围为5-7(3个*)。
以此类推,i每增加1,的数量增加2(奇数递增),最终第7行(i=6)打印13个(整行)。 - 下半部分(倒金字塔)
外层循环i从6到1(共6行),控制倒金字塔的层数。
内层循环j同样从0到12,通过条件j >= 6 - i + 1 && j <= 6 + i - 1调整的范围:
当i=6时,j范围为1-11(11个)。
当i=5时,j范围为2-10(9个*)。
以此类推,i每减少1,的数量减少2(奇数递减),最终第1行(i=1)仅中间1个。
deep seek改进后的代码:
for (int k = 0; k < totalRows; k++)
{
int d = abs(k - midRow); // 当前行与中间行的距离
int start = d; // 星号起始列索引
int end = totalCols - 1 - d; // 星号结束列索引(因为列索引从0到12)
for (int j = 0; j < totalCols; j++)
{
if (j >= start && j <= end)
printf("*");
else
printf(" ");
}
printf("\n");
}
return 0;
核心思路
沙漏图案的本质是上下对称:上半部分从中间向上下扩展,下半部分从中间向上下收缩。改进代码通过计算当前行与中间行的距离(d),直接推导出该行需要打印的星号(*)范围,无需分别处理上半部分和下半部分。
题目三: 求出0~100000之间的所有“水仙花数”并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1 ^ 3+5 ^ 3+3 ^ 3,则153是一个“水仙花数”。
#include<math.h>
int main()
{
for (int i = 0; i < 100000; i++)
{
int sum = 0;
int n = 0;
int num = i;
for (int temp = i; temp > 0; temp = temp/ 10)
{
n++;
}
int temp = i;
while (temp)
{
num = temp % 10;
sum += pow(num, n);
temp = temp / 10;
}
if (i == sum && i != 0) { // 跳过0
printf("%d ", i);
}
}
}
这段代码用于找出0到99999之间的所有“水仙花数”(阿姆斯特朗数),其核心逻辑是通过遍历每个数,计算其各位数字的n次方之和(n为该数的位数),并判断是否等于原数。以下是对代码的详细分析:
代码逻辑解读
- 遍历范围
外层循环for (int i = 0; i < 100000; i++)遍历0到99999的所有整数,确保覆盖所有可能的水仙花数(已知水仙花数在十进制中最多为5位数)。 - 计算位数n
内层第一个循环for (int temp = i; temp > 0; temp = temp/10)通过不断将数值除以10,统计其位数n。例如:
当i=153时,temp依次为153→15→1→0,循环执行3次,故n=3(153是3位数)。 - 计算各位数字的n次方和sum
第二个循环while (temp)通过取模运算(temp % 10)逐位提取数字,并计算其n次方(使用pow(num, n)),累加到sum中。例如:
当i=153时,依次提取数字3、5、1,计算3³+5³+1³=27+125+1=153,故sum=153。 - 判断水仙花数
最后通过if (i == sum && i != 0)判断当前数是否为水仙花数(跳过0,因通常讨论正整数)。若满足条件则输出。
潜在问题与改进建议
- pow函数的精度问题
pow(num, n)来自math.h,返回double类型。由于浮点数精度限制,当计算较大数的n次方时(如9^5=59049),可能出现微小误差(如59049.0000001或59048.9999999),导致i == sum判断失败。
改进方案:用整数运算替代pow,避免精度丢失。例如,计算num的n次方时,通过循环累乘:
int power(int num, int n) {
int result = 1;
for (int k = 0; k < n; k++) {
result *= num;
}
return result;
}
// 使用时:sum += power(num, n); - 效率优化
当前代码对每个数都重新计算位数n,存在重复计算。可预先按位数分组(如1位数、2位数…5位数),对每组数统一计算位数,减少冗余操作。
改进示例:
for (int n = 1; n <= 5; n++) { // 遍历1到5位数
int start = pow(10, n-1); // 起始值(如n=3时,start=100)
int end = pow(10, n) - 1; // 结束值(如n=3时,end=999)
for (int i = start; i <= end; i++) { // 仅遍历n位数
// 计算sum…
}
} - 特殊情况处理
代码中i != 0的条件会跳过0。根据数学定义,0的各位数字的1次方和为0(0^1=0),因此0可视为水仙花数。若需包含0,可移除该条件。
测试用例验证
153:3位数,1³+5³+3³=153,应输出。
370:3位数,3³+7³+0³=370,应输出。
9474:4位数,9⁴+4⁴+7⁴+4⁴=9474,应输出。
54748:5位数,5⁵+4⁵+7⁵+4⁵+8⁵=54748,应输出。
结论
代码逻辑正确,能够准确找出0到99999之间的所有水仙花数。但需注意pow函数的精度问题,建议用整数运算替代以提高可靠性。优化后可进一步提升效率并覆盖更多边界情况。
—————————————————————————————————————————————
题目四:求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字
例如:2+22+222+2222+22222
代码如下:
int main()
{
int n = 5;
int sum = 0;
int a = 0;
scanf("%d", &a);
while (n)
{
sum = sum + a;
a = a * 10 + (a % 10);
n--;
}
printf("%d\n", sum);
printf("%d", a);
}
这段代码用于计算数列 Sn=a+aa+aaa+aaaa+aaaaaS_n = a + aa + aaa + aaaa + aaaaaSn=a+aa+aaa+aaaa+aaaaa 的前5项之和(其中 aaa 是一个数字,例如 a=2a=2a=2 时,数列为 2+22+222+2222+222222 + 22 + 222 + 2222 + 222222+22+222+2222+22222)。以下是对代码的详细解析:
代码逻辑分析
- 变量初始化
n = 5:表示需要计算前5项的和。sum = 0:累加和的初始值。a:用户输入的数字(0-9),用于生成数列的每一项。 - 核心循环逻辑
通过 while (n) 循环迭代5次(n 从5递减到0),每次循环完成两个关键操作:
累加当前项:将当前的 a 加到 sum 中。生成下一项:通过 a = a * 10 + (a % 10) 生成数列的下一项。 - 生成下一项的原理
假设当前项为 aka_kak(例如 a=2a=2a=2 时,第一项是 222),生成下一项 ak+1a_{k+1}ak+1 的逻辑如下:
a%10a \% 10a%10:提取当前项的最后一位数字(例如 a=22a=22a=22 时,22%10=222 \% 10 = 222%10=2)。a∗10a * 10a∗10:将当前项左移一位(相当于乘以10,例如 22∗10=22022 * 10 = 22022∗10=220)。两者相加:a∗10+(a%10)a * 10 + (a \% 10)a∗10+(a%10) 得到下一项(例如 220+2=222220 + 2 = 222220+2=222)。这一操作巧妙地利用了数字的位运算,无需字符串拼接即可生成连续的 aa…a 形式数。
示例验证(以 a=2a=2a=2 为例)
最终输出 sum=24690,与预期结果 2+22+222+2222+22222=246902+22+222+2222+22222=246902+22+222+2222+22222=24690 完全一致。
潜在问题与注意事项
输入限制:代码假设用户输入的 a 是单个数字(012+122+1222+...12 + 122 + 1222 + ...12+122+1222+...),但题目明确 a 是数字,因此无需额外处理。
数据类型溢出:当 a=9 时,最大项为 999999999999999,前5项和为 9+99+999+9999+99999=1111059+99+999+9999+99999=1111059+99+999+9999+99999=111105,这在 int 类型范围内(32位 int 最大约为 231−1=21474836472^{31}-1=2147483647231−1=2147483647)。若 n 更大(如 n=10),可能导致溢出,但题目中 n 固定为5,因此无风险。
输出多余值:代码最后额外输出了 a 的值(循环结束后 a 为第6项),这可能是调试残留,实际使用中可根据需求删除。
结论
代码逻辑正确,通过巧妙的位运算生成数列的每一项,并准确计算前5项的和。输入单个数字时(0-9),能正确输出预期结果。若需严格符合题目要求,可添加输入校验(确保 a 是0-9的数字),并删除最后多余的 printf(“%d”, a)。