练习作业4: 和8有关的数字 , 不当老大 , 幂字符串
前排提示,本次作业的基础讲解很细致,弄清楚这个卡口会对C语言学习有极大帮助!
问题 A: 和8有关的数字
序号:1099
题目描述
把1到n之间(包含n),符合以下条件之一的数字,依次打印出来。
①含有数字8
②各个数位上的数字和是8
③是8的倍数
输入
整数n,n>1
输出
符合条件的所有整数清单,一行
样例输入
30
样例输出
8 16 17 18 24 26 28
思路过程:
本题考验函数的使用和输出格式要求。首先读题,要求输入n(n>1),输出满足三个条件任意一条的数字,这里第二条注意是各个位,不是个位!
我们看输出,每两个数之间有空格,头尾都没有,这就意味着我们不能使用%d(空格)
或者 (空格)%d
一路输出到底,先选第一种,输出%d(空格)
,那么,当输出最后一个数时,我们只输出%d
即可,问题变成了如何确定输出的最后一个数?我们知道,我们输入的n是控制结束的,我们无法知道最后一个符合条件的数是多少,所以这条路不行!我们换第二条,输出(空格)%d
,只有第一个数是仅%d
,所以,问题又变成了怎样判断输出的是第一个数,这个简单,设置一个flag,flag为初值,即为第一个数,往后flag变化,输出(空格)%d
就好啦。
void put(int i,int flag)//flag的变化在主函数中体现
{
if(flag==0)
printf("%d",i);
else printf(" %d",i);
}
本题有一个,算是漏洞的地方,因为8一定是第一个输出的数,所以输出加一条if(i==8) printf("%d");
然后后面输出就是printf(" %d");
了,这样确实判断机给过的,但是希望同学能真正懂这块输出该怎么判断与如何掌握输出规则。
接下来是函数问题,为了增加函数的可读性,我们需要将函数拆分,有三个判断函数,我们就定义三个函数,加上主函数和输出函数共五个。三个判断函数不是很难,主要是主函数的调用,这里我们写一个判断语句,既然是三个条件之一满足,那我们不妨让每个判断函数都返回一个值,再用if判断,这里精髓的判断条件用或语句(||
)完成,如果其一成立,即可调用输出函数。
if(judgement1(i)||judgement2(i)||judgement3(i))//三个函数都返回0或1,成立其一即可输出
put(i,flag++);//这里体现了flag的变化!
这样,本题的难点就解决了。
附:
由于有部分同学不理解函数形式,对于这三个函数,我们稍微来回顾一下吧
第一个:判断数字为8倍数
最简单的
if(i%8==0) return 1;
else return 0;
第二个:判断数字中含有数字8
首先有定义int i=*判定数* , p
,我们采用标准最简的方法,逐个对i的最后一位进行判断,放入while循环,先不看循环条件,我们看循环体,取出i的最后一位放到p中,判断p是不是8,是就表明是8,不是就不是(那当然233),如果不是那就i去掉最后一位,由于整数的整除性,我们只要让i/10即可,然后判断条件就是i不为0就继续。如果i被除到0了还没有符合条件的,那就是i不符合了。
while(i!=0)
{
p=i%10;
if(p==8) return 1;//表示是
i/=10;
}
return 0;//表示不是
第三个:判断数字各个位和为8
首先有定义int i=*判定数*,s=0
,还是用类似上面的方法,逐个取i的最后一位,由于要判断数字各个位和是否为8,我们就要把各个数字相加存到s里
while(i!=0)//直到i被除尽
{
s+=i%10;//取i最后一位加到s里,s有初值0
i/=10;
}
if(s==8) return 1;//判断是
else return 0;//判断否
最后再次提醒!函数是灵活的!不要一条主函数写到底,不管是函数的可读性还是结构性都会大受打击!而且以后工作中这样的代码不利于维护,更会体现出个人的水平的高低。
Example Answer:
#include<stdio.h>
void put(int i,int flag)//输出函数
{
if(flag==0)//判断flag,即是否为第一个输出数
printf("%d",i);//由于flag初值为0,即如果flag为0就是第一个数
else printf(" %d",i);
}
int judgement1(int i)//判断是否各个位上含有8
{
int p;
while(i!=0)
{
p=i%10;
if(p==8) return 1;
i/=10;
}
return 0;
}
int judgement2(int i)//判断是否各位和为8
{
int s=0;
while(i!=0)
{
s+=i%10;
i/=10;
}
if(s==8) return 1;
else return 0;
}
int judgement3(int i)//判断是否为8倍数
{
if(i%8==0) return 1;
else return 0;
}
int main()
{
int i,n,flag=0;//flag初值为0
scanf("%d",&n);
for(i=1;i<=n;i++)
{
if(judgement1(i)||judgement2(i)||judgement3(i))//精髓的 或 语句
put(i,flag++);
}
return 0;
}
问题 B: 不当老大
序号:1056
题目描述
“当老大有什么意思!”小明如是想。于是他把班级里的同学按年龄排序,最为推崇第二大的同学了!然而,他也很推崇倒数第二大的同学。请问这两个同学相差多少岁?
输入
第一行是数据的组数 nCase (n<=5),每组测试数据的第一行是一个整数 n(n>=2) ,表示班级里有 n 位同学。以下是 n 位同学的年龄。
输出
年龄第二和倒数第二的同学的年龄差。
样例输入
2
50
16 19 4 25 20 5 28 28 30 26 13 18 2 14 1 30 14 2 3 17 6 6 24 16 16 30 2 29 16 27 9 29 24 7 14 5 7 6 13 1 21 1 18 8 5 6 2 7 14 7
20
9 29 24 7 14 5 7 6 13 1 21 1 18 8 5 6 2 7 14 7
样例输出
29
23
提示
年龄差不能是负数
思路过程:
本题与第一期“笨鸟先飞”相同点,先输入有几组数据,然后输入第一组的数据的个数,再输入第一组数据,接着输入第二组数据的个数,再输入第二组数据,重复直到第n组。
不当老大的要求,就是不当最大最小的那个数,那么最大最小的那个数要不要找出来?答案是要的,那么找出来是取这个数,还是取它的下标,本题有多种解法,我们继续分析。
我们先输入Case,然后输入数据个数n,这里直接定义数组a[n],然后用for循环赋值输入数据了。
scanf("%d",&Case);
while(Case--)
{
scanf("%d",&n);
int a[n];
for(i=0;i<n;i++)
scanf("%d",&a[i]);
...未完
接着,我们写cal函数来进行计算,传递参数为数据个数和数组a,我们先遍历数组,找出最小最大值。取出了下标(***后皆为下标!***)。
for(i=0;i<n;i++)
if(a[max1]<a[i])
max1=i;
for(i=0;i<n;i++)
if(a[min1]>a[i])
min1=i;
接着给第二大第二小的变量赋值,这里我把最大数赋给了第二小的数,把最小数赋给了第二大的数,一定要注意赋值考虑会不会对接下来的计算有影响。我的想法是因为这样交叉赋值,还赋了最值,比如说第二大的初值我赋了最小值,接下来的判断中总会有值能覆盖掉它的。
max2=min1;
min2=max1;
然后如果值小于最大值且大于其他任何值,那就把它下标给第二大数,第二小数亦然。注意,如果下标和最大值最小值相同要continue,因为如果最大值和第二大值都相同,这里判断会出错。第二大第二小值可以和最大值最小值相等,如果存在的话!(吐槽出题人,这叫老二嘛= =)
注意看if(a[max2]<a[i]&&a[i]<=a[max1]) max2=i;
第二大值比当前数小,且当前数小于等于(<=)最大值,那这个数就要取代原第二大值,这里就给了下标。第二小值同理。
贴上代码帮助理解
for(i=0;i<n-1;i++)
{ if(i==max1) continue;
if(a[max2]<a[i]&&a[i]<=a[max1]) max2=i;
}
for(i=0;i<n-1;i++)
{
if(i==min1) continue;
if(a[min2]>a[i]&&a[i]>=a[min1]) min2=i;
}
最后返回两值相减,提示中告诉我们
年龄差不能是负数
所以记得加上绝对值abs和头文件math.c。其实第二大减第二小就是正数…
测试的时候发现问题,一定要加绝对值abs!否则不给过。
问题就解决啦。
以上是原数列直接找值的方法,本题还有另一种思路,也比我先想的这种好实现一点,那就是排序!
前部分代码相同,后面我们可以对数列进行排序
int i,j,t;
for(i=0;i<n-1;i++)
for(j=0;j<n-1-i;j++)
if(a[j]>=a[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
有同学不清楚排序,这里展开讲一下:
这是冒泡法排序,i=0第一次大循环,j进行第一次小循环,如果一个数比它后面的数大,就把它和后面的数交换。这样第一次小循环走完最后一个数一定是最大的数!然后第二次大循环开始,第二次小循环做到n-1-i,也就是说做到了上一次循环的前一位,而上一次循环会把这次循环最大的值放最后,如此一来,大循环结束时,数列排序已经完成了。
这样a[1]就是第二小,a[n-1]就是第二大的数了。
直接在原数列找值,排序找值,都是很好的方法!
Example Answer 1:
#include<stdio.h>
#include<math.h>
int cal(int n,int a[])
{
int i,max1=0,max2,min1=0,min2,t;
for(i=0;i<n;i++)
if(a[max1]<a[i])
max1=i;
for(i=0;i<n;i++)
if(a[min1]>a[i])
min1=i;//找出最大最小值下标
max2=min1;
min2=max1;//赋初值
for(i=0;i<n-1;i++)
{ if(i==max1) continue;//注意continue
if(a[max2]<a[i]&&a[i]<=a[max1]) max2=i;
}
for(i=0;i<n-1;i++)
{
if(i==min1) continue;
if(a[min2]>a[i]&&a[i]>=a[min1]) min2=i;
}
return abs(a[max2]-a[min2]);//绝对值
}
int main()
{
int n,Case,i;
scanf("%d",&Case);
while(Case--)//用自减
{
scanf("%d",&n);
int a[n];
for(i=0;i<n;i++)
scanf("%d",&a[i]);
printf("%d\n",cal(n,a));//输出调用函数cal
}
return 0;
}
Example Answer 2:
#include<stdio.h>
#include<math.h>
void cal(int n,int *a)//这里用到了指针!
{
int i,j,t;
for(i=0;i<n-1;i++)//排序处理
for(j=0;j<n-1-i;j++)
if(a[j]>=a[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
int main()
{
int n,Case,i;
scanf("%d",&Case);
while(Case--)//用自减
{
scanf("%d",&n);
int a[n];
for(i=0;i<n;i++)
scanf("%d",&a[i]);
cal(n,a);//由于指针的帮助,函数可以直接调用且不需要返回值
printf("%d\n",abs(a[n-2]-a[1]));//输出调用函数cal,传递数组大小和数组
}
return 0;
}
问题 C: 幂字符串
序号:1193
题目描述
给你一个字符串,请你判断将此字符串转化成a^n形式的最大的n是多少。
例如:abcd=(abcd)^1,则n=1;
aaaa=a^4,则n=4;
ababab=(ab)^3,则n=3。
输入
输入包含多组测试数据。每组输入为一个字符串,长度不超过100,其中不包含空格等空白符。当输入为一个“.”时,输入结束。
输出
对于每组输入,输出将此字符串转化成a^n形式的最大的n。
样例输入
abcd
aaaa
ababab
.
样例输出
1
4
3
思路过程:
题目较为复杂,我们先仔细读题
对于每组输入,输出将此字符串转化成a^n形式的最大的n。
就是说要找最可能作为底数的a和a重复了的次数n。再看输入,以’.'结束,输出为一行一个,简单可以解决
while(strcmp(gets(s),".")!=0)
printf("%d\n",cal(s));
然后就是函数了,我们自定义函数cal,传递s地址,对字符串s折中处理,然后取步长进行判断,用到了多个for循环,最后循环结束,返回字符串s长度与取的步长的商。较为复杂,贴上代码供参考理解。
Example Answer:
#include <stdio.h>
#include <string.h>
int cal(char *s)
{
int i,j,k;
for(i=1;i<=strlen(s)/2;i++)//折中处理
{
if((strlen(s)%i)!=0) continue;
for(j=0;j<i;j++)
{
k=j+i;
while(k<strlen(s)&&s[k]==s[j])
k+=i;
if(k<strlen(s)) break;
}
if(j==i) break;
}
return strlen(s)/i;//返回字符串长度与步长的商即为n
}
int main()
{
char s[50];
while(strcmp(gets(s),".")!=0)//比较字符串
printf("%d\n",cal(s));//调用函数
return 0;
}
小结:
本次题目较前几次题目相比至少在所学内容之内了,都是可做的题目,但是更加侧重于考验算法思维, 问题ABC难度在递增,但代码长度越来越短?算法主要是考思路思维,代码实现其实只是一个小问题 。希望同学们勇于尝试,积极钻研,这样对C的学习会很有帮助!
本次的讲解是最细致的一次了,回顾了以前学到的内容,不理解的同学可以借机补习起来,这个卡口一过,自身的算法能力会有很大的提升!
如果有问题或者错误请见谅并联系我改正!一起努力学习!