三、算法举例
(一)穷举算法
特点:对问题的所有可能状态一一测试,直到找到解或全部可能状态测试过为止。
典型问题:搬砖问题
百文百鸡问题
百马百担问题
一元钱组合问题
大量数据录入问题
1.某地需要搬运砖块,已知男人一人搬3块,女人一人搬2块,小孩两人搬一块。
问用45人正好搬45块砖,有多少种搬法?
for (men = 0; men <= 45; men++)
第一种方法:使用三重循环,将男人、女人、小孩的可能状态全部列举出来。
for (women = 0; women <= 45; women++)
for (child = 0; child <= 45; child++)
if ((men+women+child45) && (men3+women2+child*0.545))
printf(“men=%d women=%d child=%d\n”, men, women, child);
第二种方法:使用两重循环,将男人、女人的可能状态全部列举出来,小孩的状态由条件推导而出。
for (men = 0; men <= 15; men++)
for (women = 0; women <= 22; women++)
{
child = 45 – women – men;
if (men * 3 + women * 2 + child * 0.5 == 45)
printf(“men=%d women=%d child=%d\n”, men, women, child);
}
(二)迭代算法特点:不断用新值取代旧值(或旧值递推出新值)的过程。
典型问题: 银行利率计算问题
人口增长问题
兔子繁殖问题(Fibonacci数列)
一元方程的迭代解法
例如:1. 现有12亿人,每年递增2%,十年后将有多少人?
初值m=12
m=m*(1+2%) 第一年后人口
m=m*(1+2%) 第二年后人口
2. 一对新生兔子,从第三月开始它们每个月都生一对兔子,按此规律,假设没有兔子死亡,一年后共有多少兔子?
x1=1,x2=1; 第一个月 第二个月
x=x1+x2 x为当前月,x2为上月, x1为上上月
x1=x2; 新值取代旧值
x2=x; 新值取代旧值
程序代码:
#include
int main()
{
long x1,x2,x,i;
x1=1;x2=1;
for(i=1;i<=12;i++)
{
if(i<3) x=1;
else
{
x=x1+x2;
x2=x1;
x1=x;
}
printf(" %5d",x);
}
}
3.二分法解方程
一元多次方程 f(x)=x3-2x2-3=0;
在函数f(x)上任取两点,须保证f(x1)和f(x2)的值异号。
x=(x1+x2)/2;
若f(x)与f(x1)同号,则x1=x; 否则 x2=x;重复直到f(x)<= ε。
答案:x=2.485544
程序代码:
#include
#define EP 1.0e-5
int main()
{
double x1,x2,x;
double f(double x);
printf(“Please input x1,x2:”);
scanf("%lf%lf",&x1,&x2);
do
{
x=(x1+x2)/2;
if(f(x1)*f(x)>0)
x1=x;
else x2=x;
}while(fabs(f(x))>EP);
printf(" x=%f\n",x);
}
double f(double x)
{
double y;
y=xxx-2xx-3;
return y;
}
double f(double x) /计算f(x)值的函数/
{
double y;
y=xxx-2xx-3;
return y;
double f1(double x) /计算f(x)导数值的函数/
{
double y;
y=3xx-4*x;
return y;
}
5.求两个正整数的最大公约数(辗转相除法)
两个正整数u和v
辗转相除 例
r=u%v r=10%4 r=4%2
u=v u=4; u=2;
v=r; v=2; v=0;
最后余数为0时,u的值即为两个数的最大公约数.
程序代码
#include
main()
{
int a,b,u,v,r;
printf(“Please input a,b:”);
scanf("%d%d",&a,&b);
u=a;v=b;
do
{
r=u%v;
u=v;
v=r;
}while(r!=0);
printf(" %d and %d gcd is %d\n",a,b,u);
}
6.输入一个正整数m,判断它是否为素数。素数:只能被1和自身整除的正整数,1不是素数,2是素数。
算法:除了1和m,不能被其它数整除。
设 i 取值 [2, m-1]
如果m不能被该区间上的任何一个数整除,即对每个i,m%i 都不为0,则m是素数
只要找到一个i,使m%i为0,则m肯定不是素数
m %2 %3 %4 %5 %(m-1)
不是素数 || =0 =0
是素数 && !=0 !=0
m不可能被大于 m/2 的数整除,故 i可取值 [2, m-1] 、 [2, m/2] 、 [2, sqrt(m)]
/* 判断正整数m是否为素数 /
#include
int main(void)
{
int i, m;
printf("Enter a number: "); /
scanf ("%d", &m);
for(i = 2; i <= m/2; i++) / 第9行 /
if(m % i == 0) break; / 若m能被某个i整除,则m不是素数,提前结束循环 /
if(i > m/2)/ 如果循环正常结束,说明m不能被任何一个i整除,则m是素数 /
printf("%d is a prime number! \n", m);
else
printf(“No!\n”); / 第15行 /
return 0;
}
8. 整数个位分解问题(从高位开始分解)
输入一个整数,从高位开始逐个数字输出,例如12345
Step 1: 12345 /10000 = 1
12345 %10000 =2345
Step 2: 2345 /1000 = 2
2345%1000 =345
Step 3: 345 /100 = 3
345%100 =45
Step 4: 45 /10 = 4
45%10 =5
Step 5: 5/1= 5
5%1=0
End
1) 如何得到10000
找输入数据对应的幂
pow=1; temp = x;
while (x != 0) {
pow = pow * 10;
x =x /10;
}
pow = pow /10;
(2) 每次循环pow缩小1/10
(3) 直到pow=0结束。
代码:
#include “stdio.h”
int main()
{
long x,pow,temp;
scanf("%ld",&x);
if(x<0)x=-x;
pow=1;temp=x;
while(x!=0)
{
pow=pow10;
x=x/10;
}
pow=pow/10;
x=temp;
while(pow!=0)
{
printf("%5d",x/pow);
x=x%pow;
pow=pow/10;
}
printf("\n");
return 0;
}
9. 整数个位分解问题(从个位开始分解)
输入一个整数,从个位开始逐个数字输出,例如12345
Step 1: 12345 %10 = 5
12345 /10 =1234
Step 2: 1234 % 10 = 4
1234/10 =123
Step 3: 123 % 10 = 3
123/10 =12
Step 4: 12 % 10 = 2
12 /10 =1
Step 5: 1% 10= 1
1/10=0
End /* 统计一个整数的位数 /
程序代码:
#include
int main(void)
{
int count, number,m; /
count = 0;
printf(“Enter a number:”); / 输入提示 /
scanf ("%d", &number);
if(number < 0) number = -number; / 将输入的负数转换为正数 /
do{
m=number%10; / 整数求余得到个位数*/
number = number / 10; /* 整除后减少一位个位数,组成一个新数 /
count ++; / 位数加1 /
printf("%5d", m); / 输出个位*/
}while(number != 0); /* 判断循环条件 */
printf("\nIt contains %d digits.\n", count);
return 0;
}
10. 使用格里高利公式求π的近似值,要求精确到最后一项的绝对值小于10–4。
代码:
/* 用格里高利公式计算π的近似值,精度要求:最后一项的绝对值小于0.0001 /
#include
#include
int main( )
{
int i, flag;
double item, pi; / pi 用于存放累加和 /
/ 循环初始化 /
flag = 1; / flag 表示第 i 项的符号,初始为正,每次循环改变符号*/
i = 1; /* 表示第 i 项的分母,初始为1 /
item = 1.0; / item 中存放第 i 项的值,初值取 1 /
pi = 0; / 置累加和 pi 的初值为0 */
/* 当|item| ≥ 0.0001时,执行循环 /
while(fabs(item) >= 0.0001)
{
item = flag * 1.0 /i; / 计算第 i 项的值 /
pi = pi + item; / 累加第 i 项的值 /
flag = -flag; / 改变符号,为下一次循环做准备 /
i= i + 2; / 分母递增2 ,为下一次循环做准备 /
}
pi = pi * 4; / 循环计算的结果是 pi/4 */
printf (“pi = %f\n”, pi);
return 0;
}
11.阶乘计算
/* 使用函数计算 1! + 2! + 3! + … + 100! /
#include
double fact (int n); /
int main(void)
{
int i;
double sum;
sum = 0;
for(i = 1; i <= 100; i++ )
sum = sum + fact (i); / 调用fact(i)求i!,共重复100次 /
printf(“1! + 2! + … + 100! = %e\n”, sum); / 用指数形式输出结果 */
return 0;
}
/* 定义求 n! 的函数 /
double fact (int n)
{
int i;
double result; / 变量 result 中存放阶乘的值 /
result = 1; / 置阶乘 result 的初值为1 /
for(i = 1; i <= n; i++) / 循环执行n次,计算n! /
result = result * i;
return result; / 把结果回送主函数 */
}