文章目录
1. 凑硬币
1.1 所有情况
int x = 0;
printf("请输入你想要凑出的元数:");
scanf("%d", &x);
for (int one = 0;one<x*10; one++)
{
for (int two = 0; two < x * 5; two++)
{
for (int five = 0; five < x * 2; five++)
{
if (one + two * 2 + five * 5 == x * 10)
{
printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
}
}
}
}
运行结果(以2元为例):
1.2 某一种情况
得到一种结果后,便跳出嵌套循环。
1>接力break
在循环中加入break,当符合条件时便终止循环。
for (int one = 0;one<x*10; one++)
{
for (int two = 0; two < x * 5; two++)
{
for (int five = 0; five < x * 2; five++)
{
if (one + two * 2 + five * 5 == x * 10)
{
printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
exit = 1;
break;
}
}
if (exit)break;
}
if (exit)break;
}
运行结果:
若循环嵌套过多,这种方法不免有些麻烦
2>goto语句
在想要跳转的地方输入同样的标号,即可实现跳转。
for (int one = 0; one < x * 10; one++)
{
for (int two = 0; two < x * 5; two++)
{
for (int five = 0; five < x * 2; five++)
{
if (one + two * 2 + five * 5 == x * 10)
{
printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
goto out;
}
}
}
}
out:
2. 平均数
对于如果求平均数,相信大家信手拈来,所以此处的平均数为:未知数量的平均数,可以利用循环输入的方式完成,同时统计输入的数字个数,便可得到结果。
while (scanf("%d", &x) !=EOF)
每输入一个数,需要敲一次回车得到当前已经输入的数值的平均。
<暂时还未想到:将所有的数值输入之后,得到一个最终值>
int count = 0;
int x = 0;
int sum = 0;
double aver = 0.0;
while (scanf("%d", &x) !=EOF)
{
count++;
sum += x;
aver = 1.0 * sum / count;
printf("当前平均值:%f\n", aver);
}
运行结果:
3. 最大公约数
3.1 枚举法
从最小的那个数开始递减,若某一个可以同时被这这个数整除,则此数为最大公约数。
int a = 0;
int b = 0;
int min = b;
scanf("%d %d", &a, &b);
if (a < b)
{
min = a;
}
while (1)
{
if (a % min == 0 && b % min == 0)
{
printf("最大公约数:%d\n", min);
break;
}
else
{
min--;
}
}
运行结果:
不难看出此种方法有些麻烦。
3.2 辗转相除
辗转相除法(欧几里得算法):以除数和余数反复做除法运算,当余数为0时,取当前算式的除数为最大公约数。
a b t
12 18 12 12/18=0(余12)
18 12 6 18/12=1(余6)
12 6 0 12/6=2(余0)
int a = 0;
int b = 0;
int temp = 0;
scanf("%d %d", &a, &b);
while (temp)
{
temp = a % b;
a = b;
b = temp;
}
printf("最大公约数:%d\n", b);
4. 素数
素数又称质数 。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。即ta的因数只有1和本身。
所以在验证ta是否为素数时,便可以从2到num-1取余,若在此范围内有一个结果为0则不是素数。
再一想,也可以从2到num/2呀!在这里以8为例:既然有2×4=8,那么也应有4×2=8,即因数一个会较大另一个会较小。
类推一下,ta的最大因数会在ta的开平方附近,所以是2-sqrt(num),这样一来,循环的次数大大减少了。
4.1确定某一数是否为素数:
若在循环体内,有一个取余的结果为0,则该数就不是素数,循环终止。最后只要比较循环次数与sqrt(num)的大小,便可知道该数是否为素数了。
int num = 0;
int i = 0;
printf("请输入要确定的数字:");
scanf("%d", &num);
for (i = 2; i <= sqrt(num); i++)
{
if (num % i == 0)
{
break;
}
}
if (i > sqrt(num))
{
printf("%d是素数\n", num);
}
else
{
printf("%d不是素数\n", num);
}
运行结果:
4.2 确定某一范围内的素数:
先输入想要确定的范围,用嵌套循环实现所有数字的判断。
此时,判断是否为素数时,并没有选用上一种方式(比较循环次数与sqrt(num)),而是借助一个新的变量:若有其他因数,则将其赋值0并终止循环。
要注意该变量设置的位置,在循环内部(每次检验一个新的数字是否为素数时,此时的IsPrime为1)
在此我还另加入一个if语句来人为的判断1是否为素数<没有想到其他好的方法,求助求助>
另,定义了一个变量来实现素数个数的统计与输出时的换行。
int num1 = 0;
int num2 = 0;
int count = 0;
printf("请输入要确定的范围:");
scanf("%d %d", &num1, &num2);
for (int j = num1; j <= num2; j++)
{
int IsPrime = 1;
if (j == 1)
{
IsPrime = 0;
}
for (int i = 2; i <= sqrt(j); i++)
{
if (j % i == 0)
{
IsPrime = 0;
break;
}
}
if (IsPrime)
{
printf("%2d",j);
count++;
if (count % 10 == 0)
{
printf("\n");
}
else
{
printf(" ");
}
}
}
printf("\n共有%d个\n", count);
运行结果(以1到100为例):
4.3确定前n个素数
变量j为素数的当前个数,每当输出一个素数,j加1。变量n为数字开始值,即从2开始开始确定素数。
int num = 0;
printf("请输入要显示的素数个数:");
scanf("%d", &num);
int n = 2;
for (int j = 0; j < num; n++)
{
int IsPrime = 1;
for (int i = 2; i <= sqrt(n); i++)
{
if (n % i == 0)
{
IsPrime = 0;
break;
}
}
if (IsPrime)
{
printf("%d ", n);
j++;
}
if (j % 10 == 0)
{
printf("\n");
}
}
运行结果:
除此之外,对于素数还有很多种确定玩法,如:某个数字之后的n个素数是;某个数字之前的n个素数等等
5. 前n项和
分数的求和:1 + 1/2 + 1/3 + 1/4 + …
仔细看就会发现,上述式子中的分母是1逐次加一的,而分子又都是1,所以不难想到此时我们可以借助循环语句。
int n = 0;
double sum = 0.0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
sum += 1.0 / i;
}
printf("f(%d)=%f\n", n, sum);
值得注意的是,要知道这是分数的求和,所以他的值要设为double类型的,此时我们就可以知道任意n项分数的和了。
除此之外,我们可能还会遇到这种类型的式子:1 - 1/2 + 1/3 - 1/4 + … ,不难发现,此式与上述式子类似,只是分母为偶数时,该项是被减去的。
很容易就会想到,将该式子的项分为两类:奇数项和偶数项。所以可以利用if语句来求和。
再想想?“ + ”和“ - ”是交替出现的,那么利用一个变量,让ta实现的每一项的正负交换,就相当于1 + (- 1/2) + 1/3 + (- 1/4) + … 。
int n = 0;
double sum = 0.0;
int flag = 1;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
sum += flag * 1.0 / i;
flag = -flag;
}
printf("f(%d)=%f\n", n, sum);
其中,也可以将此变量改为double类型变量,这样,求和式中的1.0也就可以删去了。
6. 整数求逆
将数字的个位剥离出来,将其乘10,再加上十位…
123
123%10---3(个) 3 123/10---12
12%10---2(十) 3*10+2--32 12/10---1
1%10---1(百) 32*10+1--321 1/10---0(循环结束)
将700 -->7,即数字中的0不显示:
int num = 0;
scanf("%d", &num);
int temp = num;
int ret = 0;
while (temp > 0)
{
int a = temp % 10;
ret = a + ret * 10;
temp /= 10;
}
printf("%d逆序之后:%d", num, ret);
将700 -->007,即数字中的0显示:
int num = 0;
scanf("%d", &num);
int ret = 0;
while (num > 0)
{
int a = num % 10;
printf("%d", a);
num /= 10;
}
我们可以看到除了while循环中的操作有些许不同外,还有一处明显不同,即第一个代码中将数字赋给了另一个变量,之后的操作中使用的变量也是新的变量。
为什么呢?因为这样做,如果我们仍想要使用开始的数据时,依然可以使用。而直接对原始数据进行操作时,如果仍想显示原始数据的话,不免有些麻烦。
7.整数分解
借助之前的整数求逆,然后将逆反之后的数字从个位开始依次将数字剥离下来,即可将该整数分解。
不过,这种方法有个缺陷,那就是对于整百的数字,如100,则无法显示后面的0。用另一种整数求逆的方法(100 -->0 0 1)也无法获得,因为这种方法是将每一位的数字依次输出的。
之前的代码就是上面整数求逆的第一种方法,所以就不再重复。下面的代码则是之后的代码。
while (ret)
{
int b = ret % 10;
printf("%d", b);
ret /= 10;
if (ret)
{
printf(" ");
}
else
{
printf("\n");
}
}
正向分解数字:先确定该数字的位数,当数据类型为int时,相除可以得到该数的最高位,然后将最高位剥离之后,循环该操作。
123
123/100 - - - 1 123%100 - -23 100/10 - -10
23/10 - - - 2 23%10 - -3 10/10 - -1
3/1 - - - 3
判断位数时,要注意循环条件
int num = 0;
scanf("%d", &num);
int digits = 1;
int temp1 = num;
//确定要除的数字,判断位数
while (temp1 > 9)
{
digits *= 10;
temp1 /= 10;
}
int temp2 = num;
while (digits)
{
//开始分解
int a = temp2 / digits;
printf("%d", a);
temp2 %= digits;
digits /= 10;
//数字间用空格隔开,最后一个数字后无空格
if (digits == 0)
{
printf("\n");
}
else
{
printf(" ");
}
}
8. 生成随机数
8.1 rand函数
无参函数,返回值类型为int(整型) 头文件:<stdlib.h>
rand函数返回一个介于0和RAND_MAX(32767)之间的伪随机整数。在调用rand函数之前使用strand函数来设置伪随机数生成器。
8.2srand函数
strand函数:设置一个随机开始的起点,参数为整型
若要生成的数字随机,则需要一个变化的或是随机参数,而时间是一直在变化的。
8.3Time函数
返回值类型为整型
8.4代码
srand((unsigned int)time(NULL));
int ret = rand();
9. 多个字符从两端开始显示
9.1 代码所实现功能的简要概述
多个字符从两端移动,向中间汇聚。
最终要显示you are smart!!!!这一字符串,具体实现过程如下:
由y###############!
到yo#############!!
.....
最终you are smart!!!!
9.2 具体代码
char arr[] = { "you-are-smart!!!!" };
int sz = sizeof(arr) / sizeof(arr[0]);//计算字符串的长度
int left = 0;
int right = sz-2;//字符串以'\0'为结束标志,下标从0开始
int i = 0;
int j = 0;
for (i = 0; i < ((sz-1)/2); i++)
{
int a = left + i;
//字符从左端向中间显示
for (j = 0; j <= a; j++)
{
printf("%c", arr[j]);
}
//中间的以空格形式显示
for (i=0 ; i < (sz-1-2*(a+1)); i++)
{
printf(" ");
}
i = a;//上面的for循环已经使得i为(sz-1-2*(a+1))-1
//若没有此操作,整个大循环只会进行一次
//
//字符从右端向中间显示
int b = right - i;
for (j = (sz - 2); j >= b; j--)
{
printf("%c", arr[b+sz-2-j]);//以正常顺序显示,先显示前面的
}
printf("\n");//每显示一行就换行
}
//若字符串(我们看到的)个数为奇数个,则大循环结束后显示整个字符串
//若没有此操作,直接在循环中完成,则中间一个字符会显示2次
if (sz % 2 == 0)
{
for (i = 0; i < (sz - 1); i++)
printf("%c", arr[i]);
}
9.3 运行结果
10. 青蛙跳台阶
问题描述:
青蛙每次可以1或2个台阶,问:青蛙跳n阶台阶,共有多少次跳法?
10.1 解题思路
台阶数(n) | 方法 |
---|---|
1 | 1 —(1) |
2 | 1/1,2—(2) |
3 | 1/2,1/1/1,2/1—(3) |
4 | 1/1/2,2/2,1/2/1,1/1/1/1,2/1/1—(5) |
5 | 1/2/2,1/1/1/2,2/1/2,1/1/2/1,2/2/1,1/2/1/1,1/1/1/1/1,2/1/1/1—(8) |
… | … |
当台阶数为1时,只有一种跳法;
当台阶数为2时,可以1个1个的跳,也可以一次跳2个。
当台阶数为3时,可以这样想:n=1的跳法 + 一次跳2个,n=2的跳法 + 一次跳1个。因为台阶数为1和2的跳法,我们已经全部考虑到了,所以在n=1和2的基础上,分别跳2个和1个即可。
10.2 思路回归
可以联想到斐波那契数列【1,1,2,3,5,8,13,21,34,55…】
斐波那契数列是,第n个数=n-1个数+n-2个数(n>2)
这与青蛙跳台阶的思路是一致的。
10.3 代码
int Frog(int n)
{
if(n==1)
return 1;
else if(n==2)
return 2;
else
return Fib(n-1)+Fib(n-2);
}
不过这种方法在台阶数较小的时候,可以很方便的计算出结果。但是当台阶数过大时,会造成很多重复的运算。以斐波那契数为例,创建变量来观察一个重复的运算有多少次。
int c=0;
int Fib(int n)
{
//得到第n个斐波那契数时,第5个斐波那契数会计算多少次
if(n==5)
c++;
if(n<2)
return 1;
else
return Fib(n-1)+Fib(n-2);
}
可以看到,当我们想要知道第30个斐波那契数是多少时,第5个斐波那契数需要计算121393次,这是一个非常大的数字。因此在计算较大的数时,我们需要改变一下思路。
这种递归的思路是,为了计算当前值,我们不得不去计算上一个值和上上一个值,以此类推直到我们需要的这2个值是n=1和n=2。
而我们人工手算斐波那契数时,是知道了n=1和n=2的值,两者相加得到n=3的值,依次计算,直至得到想要的斐波那契数,所以我们也可按照这种思路来得到一个新的程序。
int Fib(int n)
{
int a=1;//第1个(n-2)
int b=1;//第2个(n-1)
int c=1;//第n个
while(n>2)
{
c=a+b;//按照计算规则,(n-1)+(n-2)得到第n个
a=b;//上一次运算中的(n-1),在这一次运算中成为(n-2)
b=c;//上一次运算中的(n),在这一次运算中成为(n-1)
n--;//计算第n个,共需要n-2次加法运算;
}
return c;
}