写在前面:这篇博客的代码部分其实很早之前就写好了。
写这篇博客的背景:舍友这两年都有参加蓝桥杯,看他这么勤奋、努力,相比于自己的平平无奇,心里不是滋味。
写这篇博客的缘由:在今年蓝桥杯成绩出来后得知他又得了二等奖,我向他要了试题的链接 2019年第十届蓝桥杯大学生B组C/C++真题,看了一下题目前面的都不会…直到看到了试题F,感觉自己可以做,于是当晚就写好了(本来打算当晚就发表出去的…)。写完后给舍友看,舍友指出了一些问题,那时候我就觉得自己写的很没有技术含量(卑微),于是乎就没有然后了。
直到昨天才发现原来舍友有写博客的习惯,进去他的博客看了一下,30篇原创,一直默默地写自己的心得、体会,把自己的点点滴滴都写进去,这时我才发现,写博客从来都不是写给谁看的(虽然被很多人看到会很有成就感 ,嘻嘻),重要的是自己总结不断成长。这才有了这第一篇自己的博客。
C语言实现,试题F:特别数的和
【问题的描述】
小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。
请问,在 1 到 n 中,所有这样的数的和是多少?
【输入格式】
输入一行包含两个整数 n。
【输出格式】
输出一行,包含一个整数,表示满足条件的数的和。
【样例输入】
40
【样例输出】
574
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 10。
对于 50% 的评测用例,1 ≤ n ≤ 100。
对于 80% 的评测用例,1 ≤ n ≤ 1000。
对于所有评测用例,1 ≤ n ≤ 10000。
思路
看到题目脑袋里就闪过if(),通过穷举的方式列出各种情况下含有2、0、1、9的情况,然后把符合情况的数字相加,最后得到的结果就是特别数的和。
代码
#include<stdio.h>
//输出1~10000内任意含有2、0、1、9的数字
int main()
{
//number为输入的数字
//i用来验证是否含有2、0、1、9
//sum为输出特别数的和
int number = 1;
int i = 1;
int sum = 0;
//从键盘读取一个数的值赋给number
printf("输入一个整数n: \n");
scanf("%d",&number);
//打印1-number里符合要求的数
printf("含有2、0、1、9的整数有: ");
for(i;i<=number;i++)
{
int sum_g = i%10;//个位数
int sum_s = i/10;//十位数
int sum_b = i/100;//百位数
int sum_q = i/1000;//千位数
//1~9里个位数含有2、0、1、9
if(i<10)
{
if(sum_g==2||sum_g==0||sum_g==1||sum_g==9)
{
sum += i ;
printf("%d ",i);
}
}
//1~99里十位数含有2、0、1、9
if(i>=10&&i<100)
{
//十位数含有2、0、1、9的数
if(sum_s==2||sum_s==0||sum_s==1||sum_s==9)
{
sum += i ;
printf("%d ",i);
}
//十位数不含2、0、1、9的数,但个位数含有2、0、1、9的数
if(!(sum_s==2||sum_s==0||sum_s==1||sum_s==9))
{
if(sum_g==2||sum_g==0||sum_g==1||sum_g==9)
{
sum += i ;
printf("%d ",i);
}
}
}
//1~999里十位数含有2、0、1、9
if(i>=100&&i<1000)
{
//百位数含有2、0、1、9的数
if(sum_b==2||sum_b==0||sum_b==1||sum_b==9)
{
sum += i ;
printf("%d ",i);
}
//百位数不含2、0、1、9的数,但个、十位数含有2、0、1、9的数
if(!(sum_b==2||sum_b==0||sum_b==1||sum_b==9))
{
int j = i - i/100*100;//转换成两位数
int sum_s_1 = j/10;
//十位数含2、0、1、9
if(sum_s_1==2||sum_s_1==0||sum_s_1==1||sum_s_1==9)
{
sum += i ;
printf("%d ",i);
}
//十位数不含2、0、1、9的数,但个位数含有2、0、1、9的数
if(!(sum_s_1==2||sum_s_1==0||sum_s_1==1||sum_s_1==9))
{
if(sum_g==2||sum_g==0||sum_g==1||sum_g==9)
{
sum += i ;
printf("%d ",i);
}
}
}
}
//1~10000里十位数含有2、0、1、9
if(i>=1000&&i<=10000)
{
//1000~9999里十位数含有2、0、1、9
if(i>=1000&&i<=9999)
{
//千位数含有2、0、1、9的数
if(sum_q==2||sum_q==0||sum_q==1||sum_q==9)
{
sum += i ;
printf("%d ",i);
}
if(!(sum_q==2||sum_q==0||sum_q==1||sum_q==9))
{
int k = i - i/1000*1000;//转换成三位数
int sum_b_1 = k/100;
int j = i - i/100*100;//转换成两位数
int sum_s_1 = j/10;
//百位数含2、0、1、9的数
if(sum_b_1==2||sum_b_1==0||sum_b_1==1||sum_b_1==9)
{
sum += i ;
printf("%d ",i);
}
//除去百位数后剩下十位与个位含2、0、1、9的数ss
if(!(sum_b_1==2||sum_b_1==0||sum_b_1==1||sum_b_1==9))
{
//十位数含2、0、1、9的数
if(sum_s_1==2||sum_s_1==0||sum_s_1==1||sum_s_1==9)
{
sum += i ;
printf("%d ",i);
}
//十位数不含2、0、1、9的数,但个位数含有2、0、1、9的数
if(!(sum_s_1==2||sum_s_1==0||sum_s_1==1||sum_s_1==9))
{
if(sum_g==2||sum_g==0||sum_g==1||sum_g==9)
{
sum += i ;
printf("%d ",i);
}
}
}
}
}
if(i==10000)
{
sum += i ;
printf("%d ",i);
}
}
}
printf("\n所有含有2、0、1、9的数的和为: %d",sum);
return 0;
}
效果图
总结
事实上这样子的代码太冗余了…
1.把所有代码都放for()循环里,导致变量i每条件符合一个if()就要循环一遍,这可能就是运行结果超过1s的原因(虽然从输入数字按回车开始,到显示求和结果结束,感觉连1s都没有)
2.好多代码都可以重复使用(例如1<=n<=100里就包含了1<=n<=10和1<=n<=100两个判断if())
改进的方法:把每个if()写成一个函数,需要用到的时候就调用(虽然主函数还是会用到if,手动滑稽),每调用一次函数就i++
终极方案:
刚刚在CSDN看到一位大佬写的代码,同样的问题,大佬只用了30行,运行起来简直比我自己写的代码好太多了!!!下面附上大佬的博客原文(←点击查看原文)
按照我的思想,通过if()判断每一位是否含有2、0、1、9,而且代码都写死了(只限于1<=n<=10000的数字,也没有判断输入是否合法)
而大佬的思想是通过一个函数来找出特别的数,然后累加求和
根据我对大佬的代码的理解,我向大佬的代码添加了一些注释。从黑盒视角看int solve(int i),solve(i)获得一个整数i,判断这个数是否含为特别的数,若是则返回1,若不是则返回0。
整个特别的数求和的过程可以简化为
1. 从键盘获得一个数n
2. 通过for()循环和solve()函数找出1~ n中特别的数然后进行累加(也可以看成通过for()循环穷举出1~n的数,然后用solve()函数筛选出特别的数,最后求和)
为了方便大家理解函数solve(),我以i=234为例,手工画了个UML图(原谅我没有画UML的软件)
//#include <iostream>
#include <stdio.h>
int solve(int n);
int main( )
{
int n,i,sum=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
if(solve(i)==1)//条件为真,加
{
sum+=i;
// printf("%d ",i);//输出每次加的数
}
}
printf("%d",sum);
return 0;
}
int solve(int i)
{
int x;
//这个while循环是用来检验输入的数字i的最后一位是否含有2、0、1、9
//而通过对i=i/10,来使小数点向左移一位,检测此时的最后一位是否含有2、0、1、9
//这样写的好处是代码简洁、效率高,和我自己写的相比...这样子写真是太完美了
//这里顺便提一下,可以用循环的地方通常都可以使用递归
while(i)
{
x=i%10;//从个位开始
if(x==2||x==0||x==1||x==9)//只要包含其中的任意一个数,结果都为真
{
return 1;
}
i=i/10;//移位
}
return 0;
}
不积硅步无以至千里,不积小流无以成江海。学习重在积累,好记性不如烂笔头,加油!