目录:
1.绘制余弦曲线
2.绘制余弦曲线和直线
3.绘制圆
4.歌星大奖赛
5.求最大数
6.高次方数的尾数
8.借书方案知多少
9.杨辉三角形
10.数制转换
11.打鱼还是晒网
12.抓交通肇事犯
13.该存多少钱
14.怎样存钱利最大
15.捕鱼和分鱼
16.出售金鱼
1.7 分数四则运算
17.平分七筐鱼
18.有限5位数
19. 8 除不尽的数
21.4位反序数
22.求车速
23.阿姆斯特朗数
24.完全数
26.亲密数
27.自守数
28.回文数
29.求具有abcd=(ab+cd)2性质的四位数
30.求素数
31.歌德巴赫猜想
32.要发就发
35.素数幻方
36.百钱百鸡问题
37.爱因斯坦的数学题
39.年龄几何
38.换分币
40.三色球问题
41.马克思手稿中的数学题
42.最大公约数和最小公倍数
43.分数比较
44.分数之和
45.将真分数分解为埃及分数
46.列出真分数序列
47.计算分数的精确值
51.谁是窃贼
52.黑与白
53.迷语博士的难题(1)
54.迷语博士的难题(2)
55.哪个大夫哪天值班
56.区分旅客国籍
57.谁家孩子跑最慢
58.拉丁方阵
59.填表格
60.1~9分成1:2:3的三个3位数
61.1~9组成三个3位的平方数
62.由8个整数形成奇特的立方体
63.减式还原
64.乘式还原
65.乘式还原(2)
66.除式还原(1)
67.除式还原(2)
68.九位累进可除数
69.魔术师的猜牌术(1)
70.魔术师的猜牌术(2)
71.约瑟夫问题
72.邮票组合
73 和数能表示1~23的5个正整数
74.可称1~40磅的4块砝码
75.10个小孩分糖果
76.小明买书
77.波松瓦酒的分酒趣题
78.求π的近似值
79.求π的近似值(2)
80.奇数平方的一个有趣性质
81.角谷猜想
82.四方定理
83.卡布列克常数
84.尼科彻斯定理
85.回文数的形成
86.自动发牌
87.黑白子交换
88.常胜将军
89.抢 30
90.搬山游戏
91.人机猜数游戏
92.人机猜数游戏(2)
93.汉诺塔
94.兎子产子
95.将阿拉伯数字转换为罗马数字
96.选美比赛
97.满足特异条件的数列
98. 八皇后问题
99.超长正整数的加法
100. 数字移动
本文转载自:http://blog.sina.com.cn/s/articlelist_1615065844_0_1.html
在屏幕上用“*”显示0~360度的余弦函数cos(x)曲线*问题分析与算法设计如果在程序中使用数组,这个问题十分简单。但若规定不能使用数组,问题就变得不容易了。
关键在于余弦曲线在0~360度的区间内,一行中要显示两个点,而对一般的显示器来说,只能按行输出,即:输出第一行信息后,只能向下一行输出,不能再返回到上一行。
为了获得本文要求的图形就必须在一行中一次输出两个“*”。 为了同时得到余弦函数cos(x)图形在一行上的两个点,考虑利用cos(x)的左右对称性。将屏幕的行方向定义为x,
列方向定义为y,则0~180度的图形与180~360度的图形是左右对称的,若定义图形的总宽度为62列,计算出x行0~180度时y点的坐标m,
那么在同一行与之对称的180~360度的y点的坐标就 应为62-m。程序中利用反余弦函数acos计算坐标(x,y)的对应关系。 使用这种方法编出的程序短小精炼,
体现了一定的技巧。
*程序说明与注释
#include<stdio.h>
#include<math.h>
void main()
{
double y;
int x,m;
for(y=1;y>=-1;y-=0.1)
{
m=acos(y)*10;
for(x=1;x<m;x++) printf(" ");
printf("*");
for(;x<62-m;x++)printf(" ");
printf("*\\n");
}
}
在屏幕上显示0~360度的cos(x)曲线与直线f(x)=45*(y-1)+31的迭加图形。其中cos(x)图形用“*”表示,f(x)用“+”表示,在两个图形相交的点上则用f(x)图形的符号。
*问题分析与算法设计
本题可以在上题的基础上进行修改。图形迭加的关键是要在分别计算出同一行中两个图形的列方向点坐标后,正确判断相互的位置关系。为此,可以先判断图形的交点,再分别控制打印两个不同的图形。
*程序注释与说明
#include<stdio.h>
#include<math.h>
void main()
{
double y;
int x,m,n,yy;
for(yy=0;yy<=20;yy++)
{
y=0.1*yy;
m=acos(1-y)*10;
n=45*(y-1)+31;
for(x=0;x<=62;x++)
if(x==m&&x==n) printf("+");
else if(x==n) printf("+");
else if(x==m||x==62-m) printf("*");
else printf(" ");
printf("\\n");
}
}
--------------------------------------------------------------------------------
在屏幕上用“*”画一个空心的圆
*问题分析与算法设计
打印圆可利用图形的左右对称性。根据圆的方程:
R*R=X*X+Y*Y
可以算出圆上每一点行和列的对应关系。
*程序说明与注释
#include<stdio.h>
#include<math.h>
void main()
{
double y;
int x,m;
for(y=10;y>=-10;y--)
{
m=2.5*sqrt(100-y*y);
for(x=1;x<30-m;x++) printf(" ");
printf("*");
for(;x<30+m;x++) printf(" ");
printf("*\\n");
}
}
在歌星大奖赛中,有10个评委为参赛的选手打分,分数为1~100分。选手最后得分为:去掉一个最高分和一个最低分后其余8个分数的平均值。请编写一个程序实现。
*问题分析与算法实现
这个问题的算法十分简单,但是要注意在程序中判断最大、最小值的变量是如何赋值的。
*程序说明与注释
#include<stdio.h>
void main()
{
int integer,i,max,min,sum;
max=-32768;
min=32767;
sum=0;
for(i=1;i<=10;i++)
{
printf("Input number %d=",i);
scanf("%d",&integer);
sum+=integer;
if(integer>max)max=integer;
if(integer<min)min=integer;
}
printf("Canceled max score:%d\\nCanceled min score:%d\\n",max,min);
printf("Average score:%d\\n",(sum-max-min)/8);
}
*运行结果
Input number1=90
Input number2=91
Input number3=93
Input number4=94
Input number5=90
Input number6=99
Input number7=97
Input number8=92
Input number9=91
Input number10=95
Canceled max score:99
Canceled min score:90
Average score:92
*思考题
题目条件不变,但考虑同时对评委评分进行裁判,即在10个评委中找出最公平(即评分最接返平均分)和最不公平(即与平均分的差距最大)的评委,程序应该怎样实现?
--------------------------------------------------------------------------------
问555555的约数中最大的三位数是多少?
*问题分析与算法设计
根据约数的定义,对于一个整数N,除去1和它自身外,凡能整除N的数即为N的约数。因此,最简单的方法是用2到N-1之间的所有数去除N,即可求出N的全部约数。本题只要求取约数中最大的三位数,则其取值范围可限制在100到999之间。
*程序说明与注释
#include<stdio.h>
void main()
{
long i;
int j;
printf("Please input number:");
scanf("%ld",&i);
for(j=999;j>=100;j--)
if(i%j==0)
{
printf("The max factor with 3 digits in %ld is:%d,\\n",i,j);
break;
}
}
*运行结果
输入:555555
输出:The max factor with 3 digits in 555555 is:777
求13的13次方的最后三位数
*问题分析与算法设计
解本题最直接的方法是:将13累乘13次方截取最后三位即可。
但是由于计算机所能表示的整数范围有限,用这种“正确”的算法不可能得到正确的结果。事实上,题目仅要求最后三位的值,完全没有必要求13的13次方的完整结果。
研究乘法的规律发现:乘积的最后三位的值只与乘数和被乘数的后三位有关,与乘数和被乘数的高位无关。利用这一规律,可以大大简化程序。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,x,y,last=1;
printf("Input X and Y(X**Y):");
scanf("%d**%d",&x,&y);
for(i=1;i<=y;i++)
last=last*x%1000;
printf("The last 3 digits of %d**%d is:%d\\n",x,y,last%1000);
}
*运行结果
Input X and Y(X**Y):13**13
The last 3 digits of 13**13 is:253
Input X and Y(X**Y):13**20
The last 3 digits of 13**20 is:801
--------------------------------------------------------------------------------
小明有五本新书,要借给A,B,C三位小朋友,若每人每次只能借一本,则可以有多少种不同的借法?
*问题分析与算法设计
本问题实际上是一个排列问题,即求从5个中取3个进行排列的方法的总数。首先对五本书从1至5进行编号,然后使用穷举的方法。假设三个人分别借这五本书中的一本,当三个人所借的书的编号都不相同时,就是满足题意的一种借阅方法。
*程序说明与注释
void main()
{
int a,b,c,count=0;
printf("There are diffrent methods for XM to distribute books to 3 readers:\\n");
for(a=1;a<=5;a++)
for(b=1;b<=5;b++)
for(c=1;a!=b&&c<=5;c++)
if(c!=a&&c!=b)
printf(count%8?"%2d:%d,%d,%d ":"%2d:%d,%d,%d\\n ",++count,a,b,c);
}
*运行结果
There are diffrent methods for XM to distribute books to 3 readers:
1: 1,2,3 2: 1,2,4 3: 1,2,5 4: 1,3,2 5: 1,3,4
6: 1,3,5 7: 1,4,2 8: 1,4,3 9: 1,4,5 10:1,5,2
11:1,5,3 12:1,5,4 13:2,1,3 14:2,1,4 15:2,1,5
16:2,3,1 17:2,3,4 18:2,3,5 19:2,4,1 20:2,4,3
21:2,4,5 22:2,5,1 23:2,5,3 24:2,5,4 25:3,1,2
26:3,1,4 27:3,1,5 28:3,2,1 29:3,2,4 30:3,2,5
31:3,4,1 32:3,4,2 33:3,4,5 34:3,5,1 35:3,5,2
36:3,5,4 37:4,1,2 38:4,1,3 39:4,1,5 40:4,2,1
41:4,2,3 42:4,2,5 43:4,3,1 44:4,3,2 45:4,3,5
46:4,5,1 47:4,5,2 48:4,5,3 49:5,1,2 50:5,1,3
51:5,1,4 52:5,2,1 53:5,2,3 54:5,2,4 55:5,3,1
56:5,3,2 57:5,3,4 58:5,4,1 59:5,4,2 60:5,4,3
在屏幕上显示杨辉三角形
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
......................................
*问题分析与算法设计
杨辉三角形中的数,正是(x+y)的N次方幂展开式各项的系数。本题作为程序设计中具有代表性的题目,求解的方法很多,这里仅给出一种。
从杨辉三角形的特点出发,可以总结出:
1)第N行有N+1个值(设起始行为第0行)
2)对于第N行的第J个值:(N>=2)
当J=1或J=N+1时:其值为1
J!=1且J!=N+1时:其值为第N-1行的第J-1个值与第N-1行第J个值之和
将这些特点提炼成数学公式可表示为:
1 x=1或x=N+1
c(x,y)=
c(x-1,y-1)+c(x-1,y) 其它
本程序应是根据以上递归的数学表达式编制的。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,j,n=13;
printf("N=");
while(n>12)
scanf("%d",&n);
for(i=0;i<=n;i++)
{
for(j-0;j<24-2*i;j++) printf(" ");
for(j=1;j<i+2;j++) printf("%4d",c(i,j));
printf("\\n");
}
}
void int c(int x,int y)
{
int z;
if((y==1)||(y==x+1)) return 1;
z=c(x-1,y-1)+c(x-1,y);
return z;
}
--------------------------------------------------------------------------------
将任一整数转换为二进制形式
*问题分析与算法设计
将十进制整数转换为二进制的方法很多,这里介绍的实现方法利用了C语言能够对位进行操作的特点。对于C语言来说,一个整数在计算机内就是 以二进制的形式存储的,所以没有必要再将一个整数经过一系列的运算转换为二进制形式,只要将整数在内存中的二进制表示输出即可。
*程序说明与注释
#include<stdio.h>
void printb(int,int);
void main()
{
int x;printf("Input number:");
scanf("%d",&x);
printf("number of decimal form:%d\\n",x);
printf(" it\'s binary form:");
printb(x,sizeof(int)*8);
putchar(\'\\n\');
}
void printb(int x,int n)
{
if(n>0)
{
putchar(\'0\'+((unsigned)(x&(1<<(n-1)))>>(n-1)));
printb(x,n-1);
}
}
*运行结果
输入:8
输出:
number of decimal form:8
it\'s bunary form:0000000000001000
输入:-8
输出:number of decimal form:-8
it\'s binary form:1111111111111000
输入:32767
输出:number of decimal form:32767
it\'s binary form:0111111111111111
输入:-32768
输出:number of decimal form:-32768
it\'s binary form:1000000000000000
输入:128
输出:number of decimal form:128
it\'s binary form:0000000010000000
中国有句俗语叫“三天打鱼两天晒网”。某人从1990年1月1日起开始“三天打鱼两天晒网”,问这个人在以后的某一天中是“打鱼”还是“晒网”。
*问题分析与算法设计
根据题意可以将解题过程分为三步:
1)计算从1990年1月1日开始至指定日期共有多少天;
2)由于“打鱼”和“晒网”的周期为5天,所以将计算出的天数用5去除;
3)根据余数判断他是在“打鱼”还是在“晒网”;
若 余数为1,2,3,则他是在“打鱼”
否则 是在“晒网”
在这三步中,关键是第一步。求从1990年1月1日至指定日期有多少天,要判断经历年份中是否有闰年,二月为29天,平年为28天。闰年的方法可以用伪语句描述如下:
如果 ((年能被4除尽 且 不能被100除尽)或 能被400除尽)
则 该年是闰年;
否则 不是闰年。
C语言中判断能否整除可以使用求余运算(即求模)
*程序与程序注释
#include<stdio.h>
int days(struct date day);
struct date{
int year;
int month;
int day;
};
void main()
{
struct date today,term;
int yearday,year,day;
printf("Enter year/month/day:");
scanf("%d%d%d",&today.year,&today.month,&today.day);
term.month=12;
term.day=31;
for(yearday=0,year=1990;year<today.year;year++)
{
term.year=year;
yearday+=days(term);
}
yearday+=days(today);
day=yearday%5;
if(day>0&&day<4) printf("he was fishing at that day.\\n");
else printf("He was sleeping at that day.\\n");
}
int days(struct date day)
{
static int day_tab[2][13]=
{{0,31,28,31,30,31,30,31,31,30,31,30,31,},
{0,31,29,31,30,31,30,31,31,30,31,30,31,},
};
int i,lp;
lp=day.year%4==0&&day.year%100!=0||day.year%400==0;
for(i=1;i<day.month;i++)
day.day+=day_tab[lp];
return day.day;
}
*运行结果
Enter year/month/day:1991 10 25
He was fishing at day.
Enter year/month/day:1992 10 25
He was sleeping at day.
Enter year/month/day:1993 10 25
He was sleeping at day
--------------------------------------------------------------------------------
一辆卡车违反交通规则,撞人后逃跑。现场有三人目击事件,但都没有记住车号,只记下车号的一些特征。甲说:牌照的前两位数字是相同的;乙说:牌照的后两位数字是相同的,但与前两位不同; 丙是数学家,他说:四位的车号刚好是一个整数的平方。请根据以上线索求出车号。
*问题分析与算法设计
按照题目的要求造出一个前两位数相同、后两位数相同且相互间又不同的整数,然后判断该整数是否是另一个整数的平方。
*程序与程序注释
#include<stdio.h>
#include<math.h>
void main()
{
int i,j,k,c;
for(i=1;i<=9;i++)
for(j=0;j<=9;j++)
if(i!=j)
{
k=i*1000+i*100+j*10+j;
for(c=31;c*c<k;c++);
if(c*c==k) printf("Lorry--No. is %d.\\n",k);
}
}
*运行结果
Lorry _No.is 7744
假设银行一年整存零取的月息为0.63%。现在某人手中有一笔钱,他打算在今后的五年中的年底取出1000元,到第五年时刚好取完,请算出他存钱时应存入多少。
*问题分析与算法设计
分析存钱和取钱的过程,可以采用倒推的方法。若第五年年底连本带息要取1000元,则要先求出第五年年初银行存款的钱数:
第五年初存款=1000/(1+12*0.0063)
依次类推可以求出第四年、第三年......的年初银行存款的钱数:
第四年年初存款=(第五年年初存款+1000)/(1+12*0.0063)
第三年年初存款=(第四年年初存款+1000)/(1+12*0.0063)
第二年年初存款=(第三年年初存款+1000)/(1+12*0.0063)
第一年年初存款=(第二年年初存款+1000)/(1+12*0.0063)
通过以上过程就可以很容易地求出第一年年初要存入多少钱。
*程序与程序注释
#include<stdio.h>
void main()
{
int i;
float total=0;
for(i=0;i<5;i++)
total=(total+1000)/(1+0.0063*12);
printf("He must save %.2f at first.\\n",total);
}
*运行结果
He must save 4039.44 at first
假设银行整存整取存款不同期限的月息利率分别为:
0.63% 期限=1年
0.66% 期限=2年
0.69% 期限=3年
0.75% 期限=5年
0.84% 期限=8年
利息=本金*月息利率*12*存款年限。
现在某人手中有2000元钱,请通过计算选择一种存钱方案,使得钱存入银行20年后得到的利息最多(假定银行对超过存款期限的那一部分时间不付利息)。
*问题分析与算法
为了得到最多的利息,存入银行的钱应在到期时马上取出来,然后立刻将原来的本金和利息加起来再作为新的本金存入银行,这样不断地滚动直到满20年为止,由于存款的利率不同,所以不同的存款方法(年限)存20年得到的利息是不一样的。
分析题意,设2000元存20年,其中1年存i1次,2年存i2次,3年存i3次,5年存i5次,8年存i8次,则到期时存款人应得到的本利合计为:
2000*(1+rate1)i1*(1+rate2)i2*(1+rate3)i3*(1+rate5)i5*(1+rate8)i8
其中rateN为对应存款年限的利率。根据题意还可得到以下限制条件:
0<=i8<=2
0<=i5<=(20-8*i8)/5
0<=i3<=(20-8*i8-5*i5)/3
0<=i2<=(20-8*i8-5*i5-3*i3)/2
0<=i1=20-8*i8-5*i5-3*i3-2*i2
可以用穷举法穷举所有的i8、i5、i3、i2和i1的组合,代入求本利的公式计算出最大值,就是最佳存款方案。
*程序与程序注释
#include<stdio.h>
#include<math.h>
void main()
{
int i8,i5,i3,i2,i1,n8,n5,n3,n2,n1;
float max=0,term;
for(i8=0;i8<3;i8++)
for(i5=0;i5<=(20-8*i8)/5;i5++)
for(i3=0;i3<=(20-8*i8-5*i5)/3;i3++)
for(i2=0;i2<=(20-8*i8-5*i5-3*i3)/2;i2++)
{
i1=20-8*i8-5*i5-3*i3-2*i2;
term=2000.0*pow((double)(1+0.0063*12),(double)i1)
*pow((double)(1+2*0.0063*12),(double)i2)
*pow((double)(1+3*0.0069*12),(double)i3)
*pow((double)(1+5*0.0075*12),(double)i5)
*pow((double)(1+8*0.0084*12),(double)i8);
if(term>max)
{
max=term;n1=i1;n2=i2;n3=i3;n5=i5;n8=i8;
}
}
printf("For maxinum profit,he should so save his money in a bank:\\n");
printf(" made fixed deposit for 8 year: %d times\\n",n8);
printf(" made fixed deposit for 5 year: %d times\\n",n5);
printf(" made fixed deposit for 3 year: %d times\\n",n3);
printf(" made fixed deposit for 2 year: %d times\\n",n2);
printf(" made fixed deposit for 1 year: %d times\\n",n1);
printf(" Toal: %.2f\\n",max);
}
*运行结果
For maxinum profit,he should so save his money in a bank:
made fixed deposit for 8 year: 0times
made fixed deposit for 5 year: 4times
made fixed deposit for 3 year: 0times
made fixed deposit for 2 year: 0times
made fixed deposit for 1 year: 0times
Total:8841.01
可见最佳的存款方案为连续四次存5年期。
*思考题
某单位对职工出售住房,每套为2万元。买房付款的方法是:
一次交清,优惠20%
从第一年开始,每年年初分期付款:
5年交清,优惠50%;
10年交清,优惠10%;
20年交清,没有优惠。
现在有人手中正好有2万元,若假定在今后20年中物价和银行利率均保持不变,问他应当选择哪种付款方式可以使应付的钱最少?
--------------------------------------------------------------------------------
A、B、C、D、E五个人在某天夜里合伙去捕鱼,到第二天凌晨时都疲惫不堪,于是各自找地方睡觉。日上三杆,A第一个醒来,他将鱼分为五 份,把多余的一条鱼扔掉,拿走自己的一份。B第二个醒来,也将鱼分为五份,把多余的一条鱼扔掉,保持走自己的一份。C、D、E依次醒来,也按同样的方法拿 走鱼。问他们合伙至少捕了多少条鱼?
*问题分析与算法设计
根据题意,总计将所有的鱼进行了五次平均分配,每次分配时的策略是相同的,即扔掉一条鱼后剩下的鱼正好分成五份,然后拿走自己的一份,余下其它的四份。
假定鱼的总数为X,则X可以按照题目的要求进行五次分配:X-1后可被5整除,余下的鱼为4*(X-1)、5。若X满足上述要求,则X就是题目的解。
*程序与程序注释
#include<stdio.h>
void main()
{
int n,i,x,flag=1;
for(n=6;flag;n++)
{
for(x=n,i=1&&flag;i<=5;i++)
if((x-1)%5==0) x=4*(x-1)/5;
else flag=0;
if(flag) break;
else flag=1;
}
printf("Total number of fish catched=%d\\n",n);
}
*运行结果
Total number of fish catched = 3121
*问题的进一步讨论
程序采用试探法,试探的初值为6,每次试探的步长为1。这是过分保守的做法。可以在进一步分析题目的基础上修改此值,增大试探的步长值,以减少试探次数。
*思考题
请使用其它的方法求解本题
买卖提将养的一缸金鱼分五次出售系统上一次卖出全部的一半加二分之一条;第二次卖出余下的三分之一加三分之一条;第三次卖出余下的四分之一加四分之一条;第四次卖出余下的五分之一加五分之一条;最后卖出余下的11条。问原来的鱼缸中共有几条金鱼?
*题目分析与算法设计
题目中所有的鱼是分五次出售的,每次卖出的策略相同;第j次卖剩下的(j+1)分之一再加1/(j+1)条。第五次将第四次余下的11条全卖了。
假定第j次鱼的总数为X,则第j次留下:
x-(x+1)/(j+1)
当第四次出售完毕时,应该剩下11条。若X满足上述要求,则X就是题目的解。
应当注意的是:"(x+1)/(j+1)"应满足整除条件。试探X的初值可以从23开始,试探的步长为2,因为X的值一定为奇数。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,j,n=0,x;
for(i=23;n==0;i+=2)
{
for(j=1,x=i;j<=4&&x>=11;j++)
if((x+1)%(j+1)==0)
x-=(x+1)/(j+1);
else {x=0;break;}
if(j==5&&x==11)
{
printf("There are %d fishes at first.\\n",i);
n=1;
}
}
}
*运行结果
There are 59 fishes at first.
*思考题
日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完后父亲说:“老大将分给你的桔子的1/8给老 二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的 桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子?
对输入的两个分数进行+、-、*、/四则运算,输出分数结果。
算法分析如下:
对分数b/a与d/c,不管哪一种运算,其运算结果均为y/x形式。对结果y/x进行化简,约去分子分母的公因数:试用i(i=1,...,y)对y,x进行试商,若能同时整除y,x,则y,x同时约去公因数i,最后打印约简的分数。
程序代码如下:
#include<stdio.h>
void main()
{
long int a,b,c,d,i,x,y,z;
char op;
printf("两分数b/a,d/c作+,-,*,/四则运算,结果为分数。\\n");
printf("请输入分数运算式。\\n");
scanf("%ld/%ld%c%ld/%ld",&b,&a,&op,&d,&c);
if(a==0||c==0) {printf("分母为0输入错误!");exit(0);}
if(op==\'+\'){y=b*c+d*a;x=a*c;}
if(op==\'-\'){y=b*c-d*a,x=a*c;}
if(op==\'*\'){y=b*d;x=a*c;}
if(op==\'/\'){y=b/c;x=a/d;}
z=x;
if(x>y) z=y;
i=z;
while(i>1)
{
if(x%i==0&&y%i==0){x=x/i;y=y/i;continue;}
i--;
}
printf("%ld/%ld%c%ld/%ld=%ld/%ld.\\n",b,a,op,d,c,y,x);
}
--------------------------------------------------------------------------------
甲、乙、丙三位鱼夫出海打鱼,他们随船带了21只箩筐。当晚返航时,他们发现有七筐装满了鱼,还有七筐装了半筐鱼,另外七筐则是空的,由于 他们没有秤,只好通过目测认为七个满筐鱼的重量是相等的,7个半筐鱼的重量是相等的。在不将鱼倒出来的前提下,怎样将鱼和筐平分为三份?
*问题分析与算法设计
根据题意可以知道:每个人应分得七个箩筐,其中有3.5筐鱼。采用一个3*3的数组a来表示三个人分到的东西。其中每个人对应数组a的一行,数组的第0列放分到的鱼的整筐数,数组的第1列放分到的半筐数,数组的第2列放分到的空筐数。由题目可以推出:
。数组的每行或每列的元素之和都为7;
。对数组的行来说,满筐数加半筐数=3.5;
。每个人所得的满筐数不能超过3筐;
。每个人都必须至少有1 个半筐,且半筐数一定为奇数
对于找到的某种分鱼方案,三个人谁拿哪一份都是相同的,为了避免出现重复的分配方案,可以规定:第二个人的满筐数等于第一个人的满筐数;第二个人的半筐数大于等于第一个人的半筐数。
*程序与程序注释
#include<stdio.h>
int a[3][3],count;
void main()
{
int i,j,k,m,n,flag;
printf("It exists possible distribtion plans:\\n");
for(i=0;i<=3;i++)
{
a[0][0]=i;
for(j=i;j<=7-i&&j<=3;j++)
{
a[1][0]=j;
if((a[2][0]=7-j-a[0][0])>3)continue;
if(a[2][0]<a[1][0])break;
for(k=1;k<=5;k+=2)
{
a[0][1]=k;
for(m=1;m<7-k;m+=2)
{
a[1][1]=m;
a[2][1]=7-k-m;
for(flag=1,n=0;flag&&n<3;n++)
if(a[n][0]+a[n][1]<7&&a[n][0]*2+a[n][1]==7)
a[n][2]=7-a[n][0]-a[n][1];
else flag=0;
if(flag)
{
printf("No.%d Full basket Semi--basket Empty\\n",++count);
for(n=0;n<3;n++)
printf(" fisher %c: %d %d %d\\n",
\'A\'+n,a[n][0],a[n][1],a[n][2]);
}
}
}
}
}
}
* 运行结果
It exists possible distribution plans:
No.1 Full basket Semi--basket Empty
fisher A: 1 5 1
fisher B: 3 1 3
fisher C: 3 1 3
No.2 Full basket Semi--basket Empty
fisher A: 2 3 2
fisher B: 2 3 2
fisher C: 3 1 3
*思考题
晏会上数学家出了一道难题:假定桌子上有三瓶啤酒,癣瓶子中的酒分给几个人喝,但喝各瓶酒的人数是不一样的。不过其中有一个人喝了每一瓶中的酒,且加起来刚好是一瓶,请问喝这三瓶酒的各有多少人?
(答案:喝三瓶酒的人数分别是2人、3人和6人)
个位数为6且能被3整除的五位数共有多少?
*题目分析与算法设计
根据题意可知,满足条件的五位数的选择范围是10006、10016。。。99996。可设基础数i=1000,通过计算i*10+6即可得到欲选的数(i的变化范围是1000~999),再判断该数能否被3整除。
*程序说明与注释
#include<stdio.h>
void main()
{
long int i;
int count=0;
for(i=1000;i<9999;i++)
if(!((i*10+6)%3))
count++;
printf("count=%d\\n",count);
}
*运行结果
count=2999
*思考题
求100到1000之间有多少个其数字之和为5的整数。
(答案:104,113,122,131,140,203,212,221,230,302,311,320,401,410,500)
一个自然数被8除余1,所得的商被8除也余1,再将第二次的商被8除后余7,最后得到一个商为a。又知这个自然数被17除余4,所得的商被17除余15,最后得到一个商是a的2倍。求这个自然数。
*题目分析与算法设计
根据题意,可设最后的商为i(i从0开始取值),用逆推法可以列出关系式:
(((i*8+7)*8)+1)*8+1=((2*i*17)+15)*18+4
再用试探法求出商i的值。
*程序说明与注释
#include<stdio.h>
void main()
{
int i;
for(i=0;;i++)
if(((i*8+7)*8+1)*8+1==(34*i+15)*17+4)
{
printf("The required number is: %d\\n",(34*i+15)*17+4);
break;
}
}
*运行结果
The required number is:199320.一个奇异的三位数
一个自然数的七进制表达式是一个三位数,而这个自然数的九进制表示也是一个三位数,且这两个三位数的数码正好相反,求这个三位数。
*题目分析与算法设计
根据题意可知,七进制和九进制表示的这全自然数的每一位一定小于7,可设其七进制数形式为kji(i、j、k的取值分别为1~6),然后设其九进制表示形式为ijk。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,j,k;
for(i=1;i<7;i++)
for(j=0;j<7;j++)
for(k=1;k<7;k++)
if(i*9*9+j*9+k==i+j*7+k*7*7)
{
printf("The special number with 3 digits is:");
printf("%d%d%d(7)=%d%d%d(9)=%d(10)\\n",k,j,i,i,j,k,i*9*9+j*9+k);
}
}
*运行结果
The special number with 3 digits is:503(7)=305(9)=248(10)
--------------------------------------------------------------------------------
设N是一个四位数,它的9倍恰好是其反序数,求N。反序数就是将整数的数字倒过来形成的整数。例如:1234的反序数是4321。
*题目分析与算法设计
可设整数N的千、百、十、个位为i、j、k、l,其取值均为0~9,则满足关系式:
(i*103+j*102+10*k+l)*9=(l*103+k*102+10*j+i)
的i、j、k、l即构成N。
*程序说明与注释
#include<stdio.h>
void main()
{
int i;
for(i=1002;i<1111;i++)
if(i%10*1000+i/10%10*100+i/100%10*10+i/1000==i*9)
printf("The number satisfied stats condition is: %d\\n",i);
}
*运行结果
The number satisfied states condition is:1089
一辆以固定速度行驶的汽车,司机在上午10点看到里程表上的读数是一个对称数(即这个数从左向右读和从右向左读是完全一样的),为95859。两小时后里程表上出现了一个新的对称数。问该车的速度是多少?新的对称数是多少?
*题目分析与算法设计
根据题意,设所求对称数为i,其初值为95589,对其依次递增取值,将i值的每一位分解后与其对称位置上的数进行比较,若每个对称位置上的数皆相等,则可判定i即为所求的对称数。
*程序说明与注释
#include<stdio.h>
void main()
{
int t,a[5];
long int k,i;
for(i=95860;;i++)
{
for(t=0,k=100000;k>=10;t++)
{
a[t]=(i%k)/(k/10);
k/=10;
}
if((a[0]==a[4])&&(a[1]==a[3]))
{
printf("The new symmetrical number kelometers is:%d%d%d%d%d\\n",
a[0],a[1],a[2],a[3],a[4]);
printf("The velocity of the car is: %.2f\\n",(i-95859)/2.0);
break;
}
}
}
*运行结果
The new symmetrical number kelometers is:95959.
The velocity of the car is:50.00
*思考题
将一个数的数码倒过来所得到的新数叫原数的反序数。如果一个数等于它的反序数,则称它为对称数。求不超过1993的最大的二进制的对称数
如果一个正整数等于其各个数字的立方和,则称该数为阿姆斯特朗数(亦称为自恋性数)。
如 407=43+03+73就是一个阿姆斯特朗数。试编程求1000以内的所有阿姆斯特朗数。
*题目分析与算法设计
可采用穷举法,依次取1000以内的各数(设为i),将i的各位数字分解后,据阿姆斯特朗数的性质进行计算和判断。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,t,k,a[3];
printf("There are follwing Armstrong number smaller than 1000:\\n");
for(i=2;i<1000;i++)
{
for(t=0,k=1000;k>=10;t++)
{
a[t]=(i%k)/(k/10);
k/=10;
}
if(a[0]*a[0]*a[0]+a[1]*a[1]*a[1]+a[2]*a[2]*a[2]==i)
printf("%5d",i);
}
printf("\\n");
}
*运行结果
There are following Armstrong number smaller than 1000:
153 370 371 407
--------------------------------------------------------------------------------
如果一个数恰好等于它的因子之和,则称该数为“完全数”。
*题目分析与算法设计
根据完全数的定义,先计算所选取的整数a(a的取值1~1000)的因子,将各因子累加于m,若m等于a,则可确认a为完全数。
*程序说明与注释
#include<stdio.h>
void main()
{
int a,i,m;
printf("There are following perfect numbers smaller than 1000:\\n");
for(a=1;a<1000;a++)
{
for(m=0,i=1;i<=a/2;i++)
if(!(a%i))m+=i;
if(m==a)
printf("%4d",a);
}
printf("\\n");
}
*运行结果
TThere are following perfect numbers smaller than 1000:
6 28 496
如果整数A的全部因子(包括1,不包括A本身)之和等于B;且整数B的全部因子(包括1,不包括B本身)之和等于A,则将整数A和B称为亲密数。求3000以内的全部亲密数。
*题目分析与算法设计
按照亲密数定义,要判断数a是否有亲密数,只要计算出a的全部因子的累加和为b,再计算b的全部因子的累加和为n,若n等于a则可判定a和b是亲密数。计算数a的各因子的算法:
用a依次对i(i=1~a/2)进行模运算,若模运算结果等于0,则i为a的一个因子;否则i就不是a的因子。
*程序说明与注释
#include<stdio.h>
void main()
{
int a,i,b,n;
printf("There are following friendly--numbers pair smaller than 3000:\\n");
for(a=1;a<3000;a++)
{
for(b=0,i=1;i<=a/2;i++)
if(!(a%i))b+=i;
for(n=0,i=1;i<=b/2;i++)
if(!(b%i))n+=i;
if(n==a&&a<b)
printf("%4d..%4d ",a,b);
}
}
*运行结果
There are following friendly--numbers pair smaller than 3000:
220.. 284 1184.. 1210 2620.. 2924
自守数是指一个数的平方的尾数等于该数自身的自然数。例如:
252=625 762=5776 93762=87909376
请求出200000以内的自守数
*题目分析与算法设计
若采用“求出一个数的平方后再截取最后相应位数”的方法显然是不可取的,因为计算机无法表示过大的整数。
分析手工方式下整数平方(乘法)的计算过程,以376为例:
376 被乘数
X 376 乘数
----------
2256 第一个部分积=被乘数*乘数的倒数第一位
2632 第二个部分积=被乘数*乘数的倒数第二位
1128 第三个部分积=被乘数*乘数的倒数第三位
----------
141376 积
本问题所关心的是积的最后三位。分析产生积的后三位的过程,可以看出,在每一次的部分积中,并不是它的每一位都会对积的后三位产生影响。总结规律可以得到:在三位数乘法中,对积的后三位产生影响的部分积分别为:
第一个部分积中:被乘数最后三位*乘数的倒数第一位
第二个部分积中:被乘数最后二位*乘数的倒数第二位
第三个部分积中:被乘数最后一位*乘数的倒数第三位
将以上的部分积的后三位求和后截取后三位就是三位数乘积的后三位。这样的规律可以推广到同样问题的不同位数乘积。
按照手工计算的过程可以设计算法编写程序。
*程序说明与注释
#include<stdio.h>
void main()
{
long mul,number,k,ll,kk;
printf("It exists following automorphic nmbers small than 200000:\\n");
for(number=0;number<200000;number++)
{
for(mul=number,k=1;(mul/=10)>0;k*=10);
kk=k*10;
mul=0;
ll=10;
while(k>0)
{
mul=(mul+(number%(k*10))*(number%ll-number%(ll/10)))%kk;
k/=10;
ll*=10;
}
if(number==mul)
printf("%ld ",number);
}
}
*运行结果
It exsts following automorphic numbners smaller than 200000:
0 1 5 6 25 76 376 625 9376 90625 109376
打印所有不超过n(取n<256) 的其平方具有对称性质的数(也称回文数)。
*题目分析与算法设计
对于要判断的数n,计算出其平方后(存于a),将a的每一位进行分解,再按a的从低到高的顺序将其恢复成一个数k(如n=13,则a=169且k=961),若a等于k则可判定n为回亠数。
*程序说明与注释
#include<stdio.h>
void main()
{
int m[16],n,i,t,count=0;
long unsigned a,k;
printf("No. number it\'s square(palindrome)\\n");
for(n=1;n<256;n++)
{
k=0;t=1;a=n*n;
for(i=1;a!=0;i++)
{
m=a%10;
a/=10;
}
for(;i>1;i--)
{
k+=m[i-1]*t;
t*=10;
}
if(k==n*n)
printf("%2d%10d%10d\\n",++count,n,n*n);
}
}
*运行结果
No. number it\'s square(palindrome)
1 1 1
2 2 4
3 3 9
4 11 121
5 22 484
6 26 676
7 101 10201
8 111 12321
9 121 14641
--------------------------------------------------------------------------------
3025这个数具有一种独特的性质:将它平分为二段,即30和25,使之相加后求平方,即(30+25)2,恰好等于3025本身。请求出具有这样性质的全部四位数。
*题目分析与算法设计
具有这种性质的四位数没有分布规律,可以采用穷举法,对所有四位数进行判断,从而筛选出符合这种性质的四位数。具体算法实现,可任取一个四位数,将其截为两部分,前两位为a,后两位为b,然后套用公式计算并判断。
*程序说明与注释
#include<stdio.h>
void main()
{
int n,a,b;
printf("There are following number with 4 digits satisfied condition\\n");
for(n=1000;n<10000;n++)
{
a=n/100;
b=n%100;
if((a+b)*(a+b)==n)
printf("%d ",n);
}
}
*运行结果
There are following numbers with 4 digits satisfied condition:
2025 3025 9801
求素数表中1~1000之间的所有素数
*问题分析与算法设计
素数就是仅能衩1和它自身整除的整数。判定一个整数n是否为素数就是要判定整数n能否被除1和它自身之外的任意整数整除,若都不能整除,则n为素数。
程序设计时i可以从2开始,到该整数n的1/2为止,用i依次去除需要判定的整数,只要存在可以整除该数的情况,即可确定要判断的整数不是素数,否则是素数。
*程序与程序注释
#include<stdio.h>
void main()
{
int n1,nm,i,j,flag,count=0;
do{
printf("Input START and END=?");
scanf("%d%d",&n1,&nm);
}while(!(n1>0&&n1<nm));
printf("...........PRIME TABLE(%d--%d)............\\n",n1,nm);
if(n1==1||n1==2)
{
printf("%4d",2);
n1=3;count++;
}
for(i=n1;i<=nm;i++)
{
if(!(i%2))continue;
for(flag=1,j=3;flag&&j<i/2;j+=2)
if(!(i%j))flag=0;
if(flag) printf(++count%15?"%4d":"%4d\\n",i);
}
}
验证:2000以内的正偶数都能够分解为两个素数之和(即验证歌德巴赫猜想对2000以内的正偶数成立)。
*问题分析与算法设计
为了验证歌德巴赫猜想对2000以内的正偶数都是成立的,要将整数分解为两部分,然后判断出分解出的两个整数是否均为素数。若是,则满足题意;否则重新进行分解和判断。
程序中对判断是否为素数的算法进行了改进,对整数判断“用从2开始到该整数的一半”改为“2开始到该整数的平方根”。原因何在请自行分析。
*程序与程序注释
#include<stdio.h>
#include<math.h>
int fflag(int n);
void main()
{
int i,n;
for(i=4;i<=2000;i+=2)
{
for(n=2;n<i;n++)
if(fflag(n))
if(fflag(i-n))
{
printf("%14d=%d+%d\\n",i,n,i-n);
break;
}
if(n==i) printf("error %d\\n",i);
}
}
int fflag(int i)
{
int j;
if(i<=1)return 0;
if(i==2)return 1;
if(!(i%2))return 0;
for(j=3;j<=(int)(sqrt((double)i)+1);j+=2)
if(!(i%j))return 0;
return 1;
}
--------------------------------------------------------------------------------
“1898--要发就发”。请将不超过1993的所有素数从小到大排成第一行,第二行上的每个素数都等于它右肩上的素数之差。编程求出:第二行数中是否存在这样的若干个连续的整数,它们的和恰好是1898?假好存在的话,又有几种这样的情况?
第一行:2 3 5 7 11 13 17......1979 1987 1993
第二行:1 2 2 4 2 4...... 8 6
*问题分析与算法设计:
首先从数学上分析该问题:
假设第一行中的素数为n[1]、n[2]、n[3]....n、...第二行中的差值为m[1]、m[2]、m[3]...m[j]...。其中m[j]为:
m[j]=n[j+1]-n[j]。
则第二行连续N个数的和为:
SUM=m[1]+m[2]+m[3]+...+m[j]
=(n[2]-n[1])+(n[3]-n[2])+(n[4]-n[3])+...+(n[j+1]-n[j])
=n[j+1]-n[1]
由此题目就变成了:在不超过1993的所有素数中是否存在这样两个素数,它们的差恰好是1898。若存在,则第二行中必有所需整数序列,其和恰为1898,。
对等价问题的求解是比较简单的。
由分析可知,在素数序列中不必包含2,因为任意素数与2的差一定为奇数,所以不必考虑。
*程序与程序注释:
#include<stdio.h>
#include<math.h>
#define NUM 320
int number[NUM];
int fflag(int i);
void main()
{
int i,j,count=0;
printf("there are follwing primes sequences in first row:\\n");
for(j=0,i=3;i<=1993;i+=2)
if(fflag(i)) number[j++]=i;
for(j--;number[j]>1898;j--)
{
for(i=0;number[j]-number>1898;i++);
if(number[j]-number==1898)
printf("(%d).%3d,.....,%d\\n",++count,number,number[j]);
}
}
int fflag(int i)
{
int j;
if(i<=1) return 0;
if(i==2) return 1;
if(!(i%2)) return 0;
for(j=3;j<=(int)(sqrt((double)i)+1);j+=2)
if(!(i%j)) return 0;
return 1;
}
*运行结果
There are follwing primes sequences in first row:
(1).89,......,1987
(2).53,......,1951
(3). 3,......,1901
求四阶的素数幻方。即在一个4X4 的矩阵中,每一个格填 入一个数字,使每一行、每一列和两条对角线上的4 个数字所组成的四位数,均为可逆素数。
*问题分析与算法设计
有了前面的基础,本题应当说是不困难的。
最简单的算法是:采用穷举法,设定4X4矩阵中每一个元素的值后,判断每一行、每一列和两条对角线上的4个数字组成的四位数是否都是可逆素数,若是则求出了满足题意的一个解。
这种算法在原理是对的,也一定可以求出满足题意的全部解。但是,按照这一思路编出的程序效率很低,在微机上几个小时也不会运行结束。这一算法致命的缺陷是:要穷举和判断的情况过多。
充分利用题目中的“每一个四位数都是可逆素数”这一条件,可以放弃对矩阵中每个元素进行的穷举的算法,先求出全部的四位可逆素数(204 个),以矩阵的行为单位,在四位可逆素数的范围内进行穷举,然后将穷举的四位整数分解为数字后,再进行列和对角线方向的条件判断,改进的算法与最初的算法 相比,大大地减少了穷举的次数。
考虑矩阵的第一行和最后一行数字,它们分别是列方向四位数的第一个数字和最后一个数字,由于这些四位数也必须是可逆素数,所以矩阵的每一行 和最后一行中的各个数字都不能为偶数或5。这样穷举矩阵的第一行和最后一行时,它们的取值范围是:所有位的数字均不是偶数或5的四位可逆数。由于符合这一 条件的四位可逆素数很少,所以这一范围限制又一次减少了穷举的次数。
对算法的进一步研究会发现:当设定了第一和第二行的值后,就已经可以判断出当前的这种组合是否一定是错误的(尚不能肯定该组合一定是正确 的)。若按列方向上的四个两位数与四位可逆数的前两位矛盾(不是其中的一种组合),则第一、二行的取值一定是错误的。同理在设定了前三行数据后,可以立刻 判断出当前的这种组合是否一定是错误的,若判断出矛盾情况,则可以立刻设置新的一组数据。这样就可以避免将四个数据全部设定好以后再进行判断所造成的低 效。
根据以上分析,可以用伪语言描述以上改进的算法:
开始
找出全部四位的可逆素数;
确定全部出现在第一和最后一行的四位可逆素数;
在指定范围 内穷举第一行
在指定范围内穷举第二行
若第一、第二、三行已出现矛盾,则继续穷举下一个数;
在指定范围内穷举第四行
判断列和对角方向是否符合题意
若符合题意,则输出矩阵;
否则继续穷举下一个数;
结束
在实际编程中,采用了很多程序设计技巧,假如设置若干辅助数组,其目的就是要最大限度的提高程序的执行效率,缩短运行时间。下面的程序运行效率是比较高的。
*程序与程序注释
#include<stdio.h>
#include<math.h>
int number[210][5];
int select[110];
int array[4][5];
int count;
int selecount;
int larray[2][200];
int lcount[2];
int num(int number);
int ok(int number);
void process(int i);
void copy_num(int i);
int comp_num(int n);
int find1(int i);
int find2(void);
int find0(int num);
void p_array(void);
void main()
{
int i,k,flag,cc=0,i1,i4;
printf("there are magic squares with invertable primes as follw:\\n");
for(i=1001;i<9999;i+=2)
{
k=i/1000;
if(k%2!=0&&k!=5&&num(i))
{
number[count][0]=i;
process(count++);
if(number[count-1][2]%2!=0&&
number[count-1][3]%2!=0&&
number[count-1][2]!=5&&
number[count-1][3]!=5)
select[selecount++]=count-1;
}
}
larray[0][lcount[0]++]=number[0][0]/100;
larray[1][lcount[1]++]=number[0][0]/10;
for(i=1;i<count;i++)
{
if(larray[0][lcount[0]-1]!=number[0]/100)
larray[0][lcount[0]++]=number[0]/100;
if(larray[1][lcount[1]-1]!=number[0]/10)
larray[1][lcount[1]++]=number[0]/10;
}
for(i1=0;i1<selecount;i1++)
{
array[0][0]=select[i1];
copy_num(0);
for(array[1][0]=0;array[1][0]<count;array[1][0]++)
{
copy_num(1);
if(!comp_num(2))
continue;
for(array[2][0]=0;array[2][0]<count;array[2][0]++)
{
copy_num(2);
if(!comp_num(3))
continue;
for(i4=0;i4<selecount;i4++)
{
array[3][0]=select[i4];
copy_num(3);
for(flag=1,i=1;flag&&i<=4;i++)
if(!find1(i))flag=0;
if(flag&&find2())
{ printf("No.%d\\n",++cc); p_array(); }
}
}
}
}
}
int num(int number)
{
int j;
if(!ok(number)) return 0;
for(j=0;number>0;number/=10)
j=j*10+number%10;
if(!ok(j)) return 0;
return 1;
}
int ok(int number)
{
int i,j;
if(number%2==0) return 0;
j=sqrt((double)number)+1;
for(i=3;i<=j;i+=2)
if(number%i==0) return 0;
return 1;
}
void process(int i)
{
int j,num;
num=number[0];
for(j=4;j>=1;j--,num/=10)
number[j]=num%10;
}
void copy_num(int i)
{
int j;
for(j=1;j<=4;j++)
array[j]=number[array[0>[j];
}
int comp_num(int n)
{
static int ii;
static int jj;
int i,num,k,*p;
int *pcount;
switch(n){
case 2:pcount=&lcount[0];p=ⅈbreak;
case 3:pcount=&lcount[1];p=&jj;break;
default:return 0;
}
for(i=1;i<=4;i++)
{
for(num=0,k=0;k<n;k++)
num=num*10+array[k];
if(num<=larray[n-2][*p])
for(;*p>=0&&num<larray[n-2][*p];(*p)--);
else
for(;p<pcount&&num>larray[n-2][*p];(*p)++);
if(*p<0||*p>=*pcount)
{
*p=0; return 0;
}
if(num!=larray[n-2][*p])
return 0;
}
return 1;
}
int find1(int i)
{
int num,j;
for(num=0,j=0;j<4;j++)
num=num*10+array[j];
return find0(num);
}
int find2(void)
{
int num1,num2,i,j;
for(num1=0,j=0;j<4;j++)
num1=num1*10+array[j][j+1];
for(num2=0,j=0,i=4;j<4;j++,i--)
num2=num2*10+array[j];
if(find0(num1)) return(find0(num2));
else return 0;
}
int find0(int num)
{
static int j;
if(num<=number[j][0])for(;j>=0&&num<number[j][0];j--);
else for(;j<count&&num>number[j][0];j++);
if(j<0||j>=count){ j=0;return 0; }
if(num==number[j][0]) return 1;
else return 0;
}
void p_array(void)
{
int i,j;
for(i=0;i<4;i++)
{
for(j=1;j<=4;j++) printf("%d ",array[j]);
printf("\\n");
}
}
--------------------------------------------------------------------------------
中国古代数学家张丘建在他的《算经》中提出了著名的“百钱买百鸡问题”:鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?
*题目分析与算法设计
设鸡翁、鸡母、鸡雏的个数分别为x,y,z,题意给定共100钱要买百鸡,若全买公鸡最多买20只,显然x的值在0~20之间;同理,y的取值范围在0~33之间,可得到下面的不定方程:
5x+3y+z/3=100
x+y+z=100
所以此问题可归结为求这个不定方程的整数解。
由程序设计实现不定方程的求解与手工计算不同。在分析确定方程中未知数变化范围的前提下,可通过对未知数可变范围的穷举,验证方程在什么情况下成立,从而得到相应的解。
*程序说明与注释
#include<stdio.h>
void main()
{
int x,y,z,j=0;
printf("Folleing are possible plans to buy 100 fowls with 100 Yuan.\\n");
for(x=0;x<=20;x++)
for(y=0;y<=33;y++)
{
z=100-x-y;
if(z%3==0&&5*x+3*y+z/3==100)
printf("%2d:cock=%2d hen=%2d chicken=%2d\\n",++j,x,y,z);
}
}
*运行结果
Follwing are possible plans to buy 100 fowls with 100 Yuan.
1:cock=0 hen=25 chicken=75
2:cock=4 hen=18 chicken=78
3:cock=8 hen=11 chicken=81
4:cock=12 hen=4 chicken=84
*总是的进一步讨论
这类求解不定方程总理的实现,各层循环的控制变量直接与方程未知数有关,且采用对未知数的取值范上穷举和组合的方法来复盖可能得到的全部各组解。能否根据题意更合理的设置循环控制条件来减少这种穷举和组合的次数,提高程序的执行效率,请读者考虑。
爱因斯坦出了一道这样的数学题:有一条长阶梯,若每步跨2阶,则最最后剩一阶,若每步跨3 阶,则最后剩2阶,若每步跨5阶,则最后剩4阶,若每步跨6阶则最后剩5阶。只有每次跨7阶,最后才正好一阶不剩。请问这条阶梯共有多少阶?
*题目分析与算法设计
根据题意,阶梯数满足下面一组同余式:
x≡1 (mod2)
x≡2 (mod3)
x≡4 (mod5)
x≡5 (mod6)
x≡0 (mod7)
*程序说明与注释
#include<stdio.h>
void main()
{
int i=1;
while(!((i%2==1)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==0)))
++i;
printf("Staris_number=%d\\n",i);
}
*运行结果
Staris_number=119
*问题的进一步讨论
此题算法还可考虑求1、2、4、5的最小公倍数n,然后判t(t为n-1)≡0(mod7)是否成立,若不成立则t=t+n,再进行判别,直至选出满足条件的t值。请自行编写程序实现。
张三、李四、王五、刘六的年龄成一等差数列,他们四人的年龄相加是26,相乘是880,求以他们的年龄为前4项的等差数列的前20项。
*题目分析与算法设计
设数列的首项为a,则前4项之和为"4*n+6*a",前4 项之积为"n*(n+a)*(n+a+a)*(n+a+a+a)"。同时"1<=a<=4","1<=n<=6"。可采用穷举法求出此数列。
*程序说明与注释
#include<stdio.h>
void main()
{
int n,a,i;
printf("The series with equal difference are:\\n");
for(n=1;n<=6;n++)
for(a=1;a<=4;a++)
if(4*n+6*a==26&&n*(n+a)*(n+a+a)*(n+a+a+a)==880)
for(i=0;i<20;i++)
printf("%d ",n+i*a);
}
*运行结果
The series with equal difference are:
2 5 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53 56 59
用一元人民币兑换成1分、2分和5分硬币,共有多少种不同的兑换方法。
*题目分析与算法设计
根据题意设i,j,k分别为兑换的1分、2分、5分硬币所具有的钱数(分),则i,j,k的值应满足:
i+j+k=100
*程序说明与注释
#include<stdio.h>
void main()
{
int i,j,k,count=1;
printf("There are follwing small exchange plans for 1 Yuan note:\\n");
for(i=0;i<=100;i++)
for(j=0;j<=100-i;j+=2)
for(k=0;k<=100-i-2*j;k+=5)
if(i+j+k==100)
printf(count%4?"%d:1*%d+2*%d+5*%d\\t":"%d:1*%d+2*%d+5*%d\\n",count++,i,j/2,k/5);
}
若一个口袋中放有12个球,其中有3个红的。3个白的和6个黒的,问从中任取8个共有多少种不同的颜色搭配?
*题目分析与算法设计
设任取的红球个数为i,白球个数为j,则黒球个数为8-i-j,根据题意红球和白球个数的取值范围是0~3,在红球和白球个数确定的条件下,黒球个数取值应为8-i-j<=6。
*程序说明与注释
#include<stdio.h>
void main()
{
int i,j,count=0;
printf(" RED BALL WHITE BALL BLACKBALL\\n");
printf("..................................................\\n");
for(i=0;i<=3;i++)
for(j=0;j<=3;j++)
if((8-i-j)<=6)
printf(" %2d: %d %d %d\\n",++count,i,j,8-i-j);
}
马克思手稿中有一道趣味数学问题:有30个人,其中有男人、女人和小孩,在一家饭馆吃饭花了50先令;每个男人花3先令,每个女人花2先令,每个小孩花1先令;问男人、女人和小孩各有几人?
*题目分析与算法设计
设x,y,z分别代表男人、女人和小孩。按题目的要求,可得到下面的方程:
x+y+z=30 (1)
3x+2y+z=50 (2)
用方程程序求此不定方程的非负整数解,可先通过(2)-(1)式得:
2x+y=20 (3)
由(3)式可知,x变化范围是0~10
*程序说明与注释
#include<stdio.h>
void main()
{
int x,y,z,count=0;
printf(" Men Women Children\\n");
printf("........................................\\n");
for(x=0;x<=10;x++)
{
y=20-2*x;
z=30-x-y;
if(3*x+2*y+z==50)
printf(" %2d: %d %d %d\\n",++count,x,y,z);
}
}
求任意两个正整数的最大公约数和(GCD)和最小公倍数(LCM)
*问题分析与算法设计
手工方式求两个正整数的蝚大公约数的方法是用辗转相除法,在程序中可以模拟这种方式。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,num1,num2,temp;
printf("Input a & b:");
scanf("%d%d",&num1,&num2);
if(num1>num2)
{
temp=num1; num1=num2; num2=temp;
}
a=num1; b=num2;
while(b!=0)
{
temp=a%b;
a=b;
b=temp;
}
printf("The GCD of %d and %d is: %d\\n",num1,num2,a);
printf("The LCM of them is: %d\\n",num1*num2/a);
}
*运行结果
1.Input a & b: 20 55
The GCD of 20 and 55 is: 5
The LCM of them is: 220
2.Input a & b: 17 71
The GCD of 17 and 71 is: 1
The LCM of them is: 1207
3.Input a & b: 24 88
The GCD of 24 and 88 is: 8
The LCM of them is: 264
4.Input a & b: 35 85
The GCD of 35 and 85 is: 5
The LCM of them is: 595
比较两个分数的大小。
*问题分析与算法设计
人工方式下比较分数大小最常用的方法是:进行分数的通分后比较分子的大小。可以编程模拟手式方式。
*程序与程序注释
#include<stdio.h>
int zxgb(int a,int b);
void main()
{
int i,j,k,l,m,n;
printf("Input two FENSHU:\\n");
scanf("%d/%d,%d/%d",&i,&j,&k,&l);
m=zxgb(j,l)/j*i;
n=zxgb(j,l)/l*k;
if(m>n) printf("%d/%d>%d/%d\\n",i,j,k,l);
else if(m==n) printf("%d/%d=%d/%d\\n",i,j,k,l);
else printf("%d/%d<%d/%d\\n",i,j,k,l);
}
int zxgb(int a,int b)
{
long int c;
int d;
if(a<b) c=a,a=b,b=c;
for(c=a*b;b!=0;)
{
d=b; b=a%b; a=d;
}
return (int)c/a;
}
*运行结果
输入: 4/5,6/7 输出: 4/5<6/7
输入: 8/4,16/32 输出: 8/4>16/32
输入:16/32,4/8 输出: 16/32=4/8
求这样的四个自然数p,q,r,s(p<=q<=r<=s),使得以下等式成立:
1/p+1/q+1/r+1/s+1
*问题分析与算法设计
若规定p<=q<=r<=s,将原式通分、化简并整理后得到:
2<=p<5 p<=q<7 q<r<13
采用最简单的穷举方法可以很方便的求解。
程序与程序注释:
#include<stdio.h>
void main()
{
int p,q,r,s,count=0;
printf("The 4 fractions which sum is equal 1 are:\\n");
for(p=2;p<5;p++)
for(q=p;q<7;q++)
for(r=q;r<13;r++)
if(p*q*r-q*r-p*r-p*q!=0)
{
s=(p*q*r)/(p*q*r-q*r-p*r-p*q);
if(!((p*q*r)%(p*q*r-q*r-p*r-p*q))&&s>=r)
printf("[%2d] 1/%d+1/%d+1/%d+1/%d=1\\n",++count,p,q,r,s);
}
}
--------------------------------------------------------------------------------
分子为1 的分数称为埃及分数,现输入一个真分数,请将该分数分解为埃及分数。
如:8/11=1/2+1/5+1/55+1/110。
*问题分析与算法设计
若真分数的分子a能整除分母b,则真分数经过化简就可以得到埃及分数,若真分数的分子不能整除分母,则可以从原来的分数中分解出一个分母为b/a+1的埃及分数。用这种方法将剩余部分反复分解,最后可得到结果。
*程序与程序注释
#include<stdio.h>
void main()
{
long int a,b,c;
printf("Please enter a optional fraction(a/b):");
scanf("%ld/%ld",&a,&b);
printf("It can be decomposed t");
while(1)
{
if(b%a)
c=b/a+1;
else{ c=b/a; a=1;}
if(a==1)
{
printf("1/%ld\\n",c);
break;
}
else
printf("1/%ld + ",c);
a=a*c-b;
b=b*c;
if(a==3)
{ printf("1/%ld + 1/%ld\\n",b/2,b); break;}
}
}
*运行结果
1. Please enter a optional fraction (a/b): 1/6
It can be decomposed t 1/6
2. Please enter a optional fraction (a/b): 20/33
It can be decomposed t 1/2+1/10+1/165
3. Please enter a optional fraction (a/b): 10/89
It can be decomposed t 1/9+1/801
4. Please enter a optional fraction (a/b): 19/99
It can be decomposed t 1/6+1/40+1/3960
5. Please enter a optional fraction (a/b): 8/89
It can be decomposed t 1/11+1/957
按递增顺序依次列出所有分母为40,分子小于40的最简分数。
*问题分析与算法设计
对分子采用穷举法,利用最大公约数的方法,判断分子与40是否构成真分数。
*程序与程序注释
#include<stdio.h>
void main()
{
int i,num1,num2,temp;
printf("The fraction serials with demominator 40 is:\\n");
for(i=1;i<=40;i++)
{
num1=40;
num2=i;
while(num2!=0)
{
temp=num1%num2;
num1=num2;
num2=temp;
}
if(num1==1)
printf("%d/40 ",i);
}
}
*运行结果
The fraction serials with demominator 40 is:
1/40 3/40 7/40 9/40 11/40 13/40 17/40 19/40
21/40 23/40 27/40 29/40 31/40 33/40 37/40 39/40
*思考题
按递增顺序依次列出所有分母小于等于40的最简真分数
--------------------------------------------------------------------------------
使用数组精确计算M/N(0<M<N<=100)的值。如果M/N是无限循环小数,则计算并输出它的第一循环节,同时要求输出 循环节的起止位置(小数位的序号)
*问题分析与算法设计
由于计算机字长的限制,常规的浮点运算都有精度限制,为了得到高精度的计算结果,就必须自行设计实现方法。
为了实现高精度的计算,可将商存放在一维数组中,数组的每个元素存放一位十进制数,即商的第一位存放在第一个元素中,商的第二位存放在第二个元素中....,依次类推。这样就可以使用数组不表示一个高精度的计算结果。
进行除法运算时可以模拟人的手工操作,即每次求出商的第一位后,将余数乘以10,再计算商的下一位,重复以上过程,当某次计算后的余数为0 时,表示M/N为有限不循环小数某次计算后的余数与前面的某个余数相同时,则M/N为无限循环小数,从该余数第一次出现之后所求得的各位数就是小数的循环 节。
程序具体实现时,采用了数组和其它一些技巧来保存除法运算所得到的余数和商的各位数。
*程序与程序注释
#include<stdio.h>
int remainder[101],quotient[101];
void main()
{
int m,n,i,j;
printf("Please input a fraction(m/n)(<0<m<n<=100):");
scanf("%d/%d",&m,&n);
printf("%d/%d it\'s accuracy value is:0.",m,n);
for(i=1;i<=100;i++)
{
remainder[m]=i;
m*=10;
quotient=m/n;
m=m%n;
if(m==0)
{
for(j=1;j<=1;j++) printf("%d",quotient[j]);
break;
}
if(remainder[m]!=0)
{
for(j=1;j<=i;j++) printf("%d",quotient[j]);
printf("\\n\\tand it is a infinite cyclic fraction from %d\\n",remainder[m]);
printf("\\tdigit to %d digit after decimal point.\\n",i);
break;
}
}
}
--------------------------------------------------------------------------------
公安人员审问四名窃贼嫌疑犯。已知,这四人当中仅有一名是窃贼,还知道这四人中每人要么是诚实的,要么总是说谎的。在回答公安人员的问题中:
甲说:“乙没有偷,是丁偷的。”
乙说:“我没有偷,是丙便的。”
丙说:“甲没有偷,是乙偷的。”
丁说:“我没有偷。”
请根据这四人的答话判断谁是盗窃者。
*问题分析与算法设计
假设A、B、C、D分别代表四个人,变量的值为1代表该人是窃贱。
由题目已知:四人中仅有一名是窃贱,且这四个人中的每个人要么说真话,要么说假话,而由于甲、乙、丙三人都说了两句话:“X没偷,X偷 了”,故不论该人是否说谎,他提到的两人中必有一人是小偷。故在列条件表达式时,可以不关心谁说谎,谁说实话。这样,可以列出下列条件表达式:
甲说:”乙没有偷,是丁偷的。” B+D=1
乙说:“我没有偷,是丙偷有。” B+C=1
丙说:“甲没有偷,是乙偷的。” A+B=1
丁说:“我没有偷。” A+B+C+D=1
其中丁只说了一句话,无法判定其真假,表达式反映了四人中仅有一名是窃贱的条件。
*程序与程序注释
#include<stdio.h>
void main()
{
int i,j,a[4];
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
if(j==i)a[j]=1;
else a[j]=0;
if(a[3]+a[1]==1&&a[1]+a[2]==1&&a[0]+a[1]==1)
{
printf("The thief is ");
for(j=0;j<=3;j++)
if(a[j])printf("%c.",j+\'A\');
printf("\\n");
}
}
}
*运行结果
The thief is B. (乙为窃贱。)
----------------------------------------------------
有A、B、C、D、E五人,每人额头上都帖了一张黑或白的纸。五人对坐,每人都可以看到其它人额头上的纸的颜色。五人相互观察后,
A说:“我看见有三人额头上帖的是白纸,一人额头上帖的是黑纸。”
B说:“我看见其它四人额头上帖的都是黑纸。”
C说:“我看见一人额头上帖的是白纸,其它三人额头上帖的是黑纸。”
D说:“我看见四人额头上帖的都是白纸。”
E什么也没说。
现在已知额头上帖黑纸的人说的都是谎话,额头帖白纸的人说的都是实话。问这五人谁的额头是帖白纸,谁的额头是帖黑纸?
*问题分析与算法设计
假如变量A、B、C、D、E表示每个人额头上所帖纸的颜色,0 代表是黑色,1 代表是白色。根据题目中A、B、C、D四人所说的话可以总结出下列关系:
A说: a&&b+c+d+e==3||!a&&b+c+d+e!=3
B说: b&&a+c+d+e==0||!b&&a+c+d+e!=0
C说: c&&a+b+d+e==1||!c&&a+b+d+e!=1
D说: d&&a+b+c+e==4||!d&&a+b+c+e!=4
穷举每个人额头所帖纸的颜色的所有可能的情况,代入上述表达式中进行推理运算,使上述表达式为“真”的情况就是正确的结果。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,c,d,e;
for(a=0;a<=1;a++)
for(b=0;b<=1;b++)
for(c=0;c<=1;c++)
for(d=0;d<=1;d++)
for(e=0;e<=1;e++)
if((a&&b+c+d+e==3||!a&&b+c+d+e!=3)
&&(b&&a+c+d+e==0||!b&&a+c+d+e!=0)
&&(c&&a+b+d+e==1||!c&&a+b+d+e!=1)
&&(d&&a+b+c+e==4||!d&&a+b+c+e!=4))
{
printf("A is pasted a piece of %s paper on his forehead.\\n",
a?"white":"black");
printf("B is pasted a piece of %s paper on his forehead.\\n",
b?"white":"black");
printf("C is pasted a piece of %s paper on his forehead.\\n",
c?"white":"black");
printf("D is pasted a piece of %s paper on his forehead.\\n",
d?"white":"black");
printf("E is pasted a piece of %s paper on his forehead.\\n",
e?"white":"black");
}
}
*运行结果
A is pasted a paper of black paper on his forehead. (黑)
B is pasted a paper of black paper on his forehead. (黑)
C is pasted a paper of white paper on his forehead. (白)
D is pasted a paper of black paper on his forehead. (黑)
E is pasted a paper of white paper on his forehead (白)
--------------------------------------------------------------------------------
诚实族和说谎族是来自两个荒岛的不同民族,诚实族的人永远说真话,而说谎族的人永远说假话。迷语博士是个聪明的人,他要来判断所遇到的人是来自哪个民族的。
迷语博士遇到三个人,知道他们可能是来自诚实族或说谎族的。为了调查这三个人是什么族的,博士分别问了他们的问题,这是他们的对话:
问第一个人:“你们是什么族?”,答:“我们之中有两个来自诚实族。”第二个人说:“不要胡说,我们三个人中只有一个是诚实族的。”第三个人听了第二个人的话后说:“对,就是只有一个诚实族的。”
请根据他的回答判断他们分别是哪个族的。
*问题分析与算法设计
假设这三个人分别为A、B、C,若说谎其值为0,若诚实,其值为1。根据题目中三个人的话可分别列出:
第一个人: a&&a+b+c==2||!a&&a+b+c!=2
第二个人: b&&a+b+c==1||!b&&a+b+c!=1
第三个人: c&&a+b+c==1||!c&&a+b+c!=1
利用穷举法,可以很容易地推出结果。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,c;
for(a=0;a<=1;a++)
for(b=0;b<=1;b++)
for(c=0;c<=1;c++)
if((a&&a+b+c==2||!a&&a+b+c!=2)
&&(b&&a+b+c==1||!b&&a+b+c!=1)
&&(c&&a+b+c==1||!c&&a+b+c!=1))
{
printf("A is a %s.\\n",a?"honest":"lier");
printf("B is a %s.\\n",b?"honest":"lier");
printf("C is a %s.\\n",c?"honest":"lier");
}
}
*运行结果
A is a lier (说谎族)
B is a lier (说谎族)
C is a lier (说谎族)
*思考题
迷语博士遇到四个人,知道他们可能是来自诚实族和说谎族的。为了调查这四个人是什么族的,博士照例进行询问:”你们是什么族的?“
第一人说:”我们四人全都是说谎族的。“
第二人说:”我们之中只有一人是说谎族的。“
第三人说:”我们四人中有两个是说谎族的。“
第四人说:”我是诚实族的。“
问自称是“诚实族”的第四个人是否真是诚实族的?
(答案:第四个人是诚实族的。)
----------------------------------------------------------
两面族是荒岛上的一个新民族,他们的特点是说话真一句假一句且真假交替。如果第一句为真,则第二句是假的;如果第一句为假的,则第二句就是真的,但是第一句是真是假没有规律。
迷语博士遇到三个人,知道他们分别来自三个不同的民族:诚实族、说谎族和两面族。三人并肩站在博士前面。
博士问左边的人:“中间的人是什么族的?”,左边的人回答:“诚实族的”。
博士问中间的人:“你是什么族的?”,中间的人回答:“两面族的”。
博士问右边的人:“中间的人究竟是什么族的?”,右边的人回答:“说谎族的”。
请问:这三个人都是哪个民族的?
*问题分析与算法设计
这个问题是两面族问题中最基本的问题,它比前面只有诚实族和说谎族的问题要复杂。解题时要使用变量将这三个民族分别表示出来。
令:变量A=1表示:左边的人是诚实族的(用C语言表示为A);
变量B=1表示:中间的人是诚实族的(用C语言表示为B);
变量C=1表示:右边的人是诚实族的(用C语言表示为C);
变量AA=1表示:左边的人是两面族的(用C语言表示为AA);
变量BB=1表示:中间的人是两面族的(用C语言表示为BB);
变量CC=1表示:右边的人是两面族的(用C语言表示为CC);
则左边的人是说谎族可以表示为:A!=1且AA!=1 (不是诚实族和两面族的人)
用C语言表示为:!A&&!AA
中间的人是说谎族可以表示为:B!=1且BB!=1
用C语言表示为:!B&&!BB
右边的人是说谎族可以表示为:C!=0且CC!=1
用C语言表示为:!C&&!CC
根据题目中“三人来自三个民族”的条件,可以列出:
a+aa!=2&&b+bb!=2&&c+cc!=2 且 a+b+c==1&&aa+bb+cc==1
根据左边人的回答可以推出:若他们是诚实族,则中间的人也是诚实族;若他不是诚实族,则中间的人也不是诚实族。以上条件可以表示为:
c&&!b&&!bb||(!c&&!cc)&&(b||bb)||!c&&cc
将全部逻辑条件联合在一起,利用穷举的方法求解,凡是使上述条件同时成立的变量取值就是题目的答案。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,c,aa,bb,cc;
for(a=0;a<=1;a++)
for(b=0;b<=1;b++)
for(c=0;c<=1;c++)
for(aa=0;aa<=1;aa++)
for(bb=0;bb<=1;bb++)
for(cc=0;cc<=1;cc++)
if(a+aa!=2&&b+bb!=2&&c+cc!=2&&
a+b+c==1&&aa+bb+cc==1 &&
(a&&!aa&&b&&!bb||!a&&!b)&&
!b &&
(c&&!b&&!bb||(!c&&!cc)&&(b||bb)||!c&cc))
{
printf("The man stand on left is a %s.\\n",
aa?"double--dealer":(a?"honest":"lier"));
printf("The man stand on left is a %s.\\n",
bb?"double--dealer":(b?"honest":"lier"));
printf("The man stand on left is a %s.\\n",
cc?"double--dealer":(c?"honest":"lier"));
}
}
*运行结果
The man stand on left is a double--dealer. (左边的人是两面族的)
The man stand on center is a lier. (中间的人是说谎族的)
The man stand on right is a honest. (右边的人是诚实族的)
*思考题
迷语博士遇到三个人,便问第一个人:“你是什么族的?”,回答:“诚实族的。”问第二个人:“你是什么族的?”,答:“说谎族的。”博士又 问第二个人:“第一个人真的是诚实族的吗?”,答:“是的。”问第三个人:“你是什么族的?”,答:“诚实族的。”博士又问第三个人:“第一个人是什么族 的?”,答:“两面族的。”
请判断这个人到底是哪个民族的?
(答案:第一个人是诚实族的,第二个人是两面族的,第三人是说谎族。)
医院有A、B、C、D、E、F、G七位大夫,在一星期内(星期一至星期天)每人要轮流值班一天。现在已知:
A大夫比C大夫晚一天值班;
D大夫比E大夫晚二天值班;
B大夫比G大夫早三天值班;
F大夫的值班日在B和C大夫的中间,且是星期四;
请确定每天究竟是哪位大夫值班?
*问题分析与算法设计
由题目可推出如下已知条件:
*F是星期四值班;
*B值班的日期在星期一至星期三,且三天后是G值班;
*C值班的日期在星期五至星期六,且一天后是A值班;
*E两天后是D值班;E值班的日期只能在星期一至星期三;
在编程时用数组元素的下标1到7表示星期一到星期天,用数组元素的值分别表示A~F七位大夫。
*程序与程序注释
#include<stdio.h>
#include<stdlib.h>
int a[8];
char *day[]={"","MONDAY","TUESDAY","WEDNESDAY","THURSDAYT",
"FRIDAY","SATUDAY","SUNDAY"};
void main()
{
int i,j,t;
a[4]=6;
for(i=1;i<=3;i++)
{
a=2;
if(!a[i+3]) a[i+3]=7;
else{ a=0;continue;}
for(t=1;t<=3;t++)
{
if(!a[t]) a[t]=5;
else continue;
if(!a[t+2]) a[t+2]=4;
else{ a[t]=0;continue;}
for(j=5;j<7;j++)
{
if(!a[j]) a[j]=3;
else continue;
if(!a[j+1]) a[j+1]=1;
else{ a[j]=0;continue;}
for(i=1;i<=7;i++)
printf("Doctor %c is on duty %s.\\n",\'A\'+a-1,day);
exit(0);
}
}
}
}
*运行结果
Doctor E is on duty MONDAY. (星期一:E)
Doctor B is on duty TUESDAY. (星期二:B)
Doctor D is on duty WEDNESDAY. (星期三:D)
Doctor F is on duty THUESDAY. (星期四:F)
Doctor G is on duty FRIDAY. (星期五:G)
Doctor C is on duty SATURDAY. (星期六:C)
Doctor A is on duty SUNDAY. (星期日:A)
*思考题
在本题的求解过程中,我们只考虑了一星期之内的情况,没有考虑跨周的情况。对于“B大夫比G大夫早三天值班的”条件只是简单的认为是在同一 周内早三天。若考虑跨周的情况就可能出现:B大夫星期一值班,而G大夫是上周的星期五。同样,对“F大夫的值班日在B和C大夫的中间”这个条件,也可以扩 展为:“只要F大夫的值班日在B和C大夫的中间就可以”。
请考虑允许跨周的情况下,可能的时间安排表。
-------------------------------------------------------
在一个旅馆中住着六个不同国籍的人,他们分别来自美国、德国、英国、法国、俄罗斯和意大利。他们的名字叫A、B、C、D、E和F。名字的顺序与上面的国籍不一定是相互对应的。现在已知:
1)A美国人是医生。
2)E和俄罗斯人是技师。
3)C和德国人是技师。
4)B和F曾经当过兵,而德国人从未参过军。
5)法国人比A年龄大;意大利人比C年龄大。
6)B同美国人下周要去西安旅行,而C同法国人下周要去杭州度假。
试问由上述已知条件,A、B、C、D、E和F各是哪国人?
*问题分析与算法设计
首先进行题目分析,尽可能利用已知条件,确定谁不是哪国人。
由:1) 2) 3)可知:A不是美国人,E不是俄罗斯人,C不是德国人。另外因为A与德国人的职业不同,E与美、德人的职业不同,C与美、俄人的职业不同,故A不是俄罗斯人或德国人,E不是美国人或德国人,C不是美国人或俄罗斯人。
由4)和5)可知B和F不是德国人,A不是法国人,C不是意大利人。
由6)可知B不是美国人,也不是法国人(因B与法国人下周的旅行地点不同);C不是法国人。
将以上结果汇总可以得到下列条件矩阵:
. 美(医生) 英 法 德(技师) 意大利 俄(教师)
A(医生) X . X X . X
B X . X X . .
C(技师) X . X X X X
D . . . . . .
E(教师) X . . X . X
F . . . X . .
根据此表使用消元法进行求解,可以方便地得到问题的答案。
将条件矩阵输入计算机,用程序实现消去算法是很容易的。
*程序与程序注释
#include<stdio.h>
char *m[7]={" ","U.S","U.K","FRANCE","GER","ITALI","EUSSIAN"};
void main()
{
int a[7][7],i,j,t,e,x,y;
for(i=0;i<7;i++)
for(j=0;j<7;j++)
a[j]=j;
for(i=1;i<7;i++)
a[0]=1;
a[1][1]=a[2][1]=a[3][1]=a[5][1]=0;
a[1][3]=a[2][3]=a[3][3]=0;
a[1][4]=a[2][4]=a[3][4]=a[5][4]=a[6][4]=0;
a[3][5]=0;
a[1][6]=a[3][6]=a[5][6]=0;
while(a[0][1]+a[0][2]+a[0][3]+a[0][4]+a[0][5]+a[0][6]>0)
{
for(i=1;i<7;i++)
if(a[0])
{
for(e=0,j=1;j<7;j++)
if(a[j]) { x=j;y=i;e++;}
if(e==1)
{
for(t=1;t<7;t++)
if(t!=i)a[x][t]=0;
a[0][y]=0;
}
}
}
for(i=1;i<7;i++)
{
printf("%c is coming from ",\'A\'-1+i);
for(j=1;j<7;j++)
if(a[j]!=0)
{ printf("%s.\\n",m[a[j>); break;}
}
}
*运行结果
A is coming from ITALY. (意大利人)
B is coming from EUSSIAN. (俄罗斯人)
C is coming from U.K.. (英国人)
D is coming from GER. (德国人)
E is coming from FRANCE. (法国人)
F is coming from U.S.. (美国人)
*问题的进一步讨论
生成条件矩阵然后使用消去法进行推理判断是一种常用的方法。对于解决较为复杂的逻辑问题是十分有效的。
*思考题
地理课上老师给出一张没有说明省份的中国地图,从中选出五个省从1到5编号,要大家写出省份的名称。交卷后五位同学每人只答了二个省份的名称如下,且每人只答对了一个省,问正确答案是什么?
A 答:2号陕西,5号甘肃 B 答:2号湖北,4号山东
C 答:1号山东,5号吉林 D 答:3号湖北,4号吉林
E 答:2号甘肃,3号陕西
张王李三家各有三个小孩。一天,三家的九个孩子在一起比赛短跑,规定不分年龄大小,跑第一得9分,跑第2得8分,依此类推。比赛结果各家的 总分相同,且这些孩子没有同时到达终点的,也没有一家的两个或三个孩子获得相连的名次。已知获第一名的是李家的孩子,获得第二的是王家的孩子。问获得最后 一名的是谁家的孩子?
*问题分析与算法设计
按题目的条件,共有1+2+3+...+9=45分,每家的孩子的得分应为15分。根据题意可知:获第一名的是李家的孩子,获第二名的是王 家的孩子,则可推出:获第三名的一定是张家的孩子。由“这些孩子没有同时到达终点的”可知:名次不能并列,由“没有一家的两个或三个孩子获得相连的名次” 可知:第四名不能是张家的孩子。
程序中为了方便起见,直接用分数表示。
*程序与程序注释
#include<stdio.h>
int score[4][4];
void main()
{
int i,j,k,who;
score[1][1]=7;
score[2][1]=8;
score[3][1]=9;
for(i=4;i<6;i++)
for(j=4;j<7;j++)
for(k=4;i!=j&&k<7;k++)
if(k!=i&&k!=j&&15-i-score[1][1]!=15-j-score[2][1]
&&15-i-score[1][1]!=15-k-score[3][1]
&&15-j-score[2][1]!=15-k-score[3][1])
{
score[1][2]=i;score[1][3]=15-i-7;
score[2][2]=j;score[2][3]=15-j-8;
score[3][2]=k;score[3][3]=15-k-9;
}
for(who=0,i=1;i<=3;i++,printf("\\n"))
for(j=1;j<=3;j++)
{
printf("%d",score[j]);
if(score[j]==1)who=i;
}
if(who==1)
printf("The last one arrived to end is a child from family Zhang.\\n");
else if(who==2)
printf("The last one arrived to end is a child from family Wang.\\n");
else printf("The last one arrived to end is a child from family Li.\\n");
}
*运行结果
7 5 3
8 6 1
9 4 2
The last one arrived to end is a child from family Wang.
(获得最后一名的是王家的孩子。)
--------------------------------------------------------------------------------
构造 NXN 阶的拉丁方阵(2<=N<=9),使方阵中的每一行和每一列中数字1到N只出现一次。如N=4时:
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
*问题分析与算法设计
构造拉丁方阵的方法很多,这里给出最简单的一种方法。观察给出的例子,可以发现:若将每 一行中第一列的数字和最后一列的数字连起来构成一个环,则该环正好是由1到N顺序构成;对于第i行,这个环的开始数字为i。按照 此规律可以很容易的写出程序。下面给出构造6阶拉丁方阵的程序。
*程序与程序注释
#include<stdio.h>
#define N 6
void main()
{
int i,j,k,t;
printf("The possble Latin Squares of order %d are:\\n",N);
for(j=0;j<N;j++)
{
for(i=0;i<N;i++)
{
t=(i+j)%N;
for(k=0;k<N;k++)
printf("%d",(k+t)%N+1);
printf("\\n");
}
printf("\\n");
}
}
*运行结果
The possble Latin Squares of order 6 are:
1 2 3 4 5 6 2 3 4 5 6 1 3 4 5 6 1 2
2 3 4 5 6 1 3 4 5 6 1 2 4 5 6 1 2 3
3 4 5 6 1 2 4 5 6 1 2 3 5 6 1 2 3 4
4 5 6 1 2 3 5 6 1 2 3 4 6 1 2 3 4 5
5 6 1 2 3 4 6 1 2 3 4 5 1 2 3 4 5 6
6 1 2 3 4 5 1 2 3 4 5 6 2 3 4 5 6 1
4 5 6 1 2 3 5 6 1 2 3 4 6 1 2 3 4 5
5 6 1 2 3 4 6 1 2 3 4 5 1 2 3 4 5 6
6 1 2 3 4 5 1 2 3 4 5 6 2 3 4 5 6 1
1 2 3 4 5 6 2 3 4 5 6 1 3 4 5 6 1 2
2 3 4 5 6 1 3 4 5 6 1 2 4 5 6 1 2 3
3 4 5 6 1 2 4 5 6 1 2 3 5 6 1 2 3 4
----------------------------------------------------------------
将1、2、3、4、5和6 填入下表中,要使得每一列右边的数字比左边的数字大,每一行下面的数字比上面的数字大。按此要求,可有几种填写方法?
. . .
. . .
*问题分析与算法设计
按题目的要求进行分析,数字1一定是放在第一行第一列的格中,数字6一定是放在第二行第三列的格中。在实现时可用一个一维数组表示,前三个元素表示第一行,后三个元素表示第二行。先根据原题初始化数组,再根据题目中填 写数字的要求进行试探。
*程序与程序注释
#include<stdio.h>
int jud1(int s[]);
void print(int u[]);
int count;
void main()
{
static int a[]={1,2,3,4,5,6};
printf("The possble table satisfied above conditions are:\\n");
for(a[1]=a[0]+1;a[1]<=5;++a[1])
for(a[2]=a[1]+1;a[2]<=5;++a[2])
for(a[3]=a[0]+1;a[3]<=5;++a[3])
for(a[4]=a[1]>a[3]?a[1]+1:a[3]+1;a[4]<=5;++a[4])
if(jud1(a)) print(a);
}
int jud1(int s[])
{
int i,l;
for(l=1;l<4;l++)
for(i=l+1;i<5;++i)
if(s[l]==s) return 0;
return 1;
}
void print(int u[])
{
int k;
printf("\\nNo.:%d",++count);
for(k=0;k<6;k++)
if(k%3==0)
printf("\\n%d",u[k]);
else
printf("%d",u[k]);
}
*运行结果
The possble table satisfied above conditions are:
No.1: No.2: No.3: No.4: No.5:
1 2 3 1 2 4 1 2 5 1 3 4 1 3 5
4 5 6 3 5 6 3 4 6 2 5 6 2 4 6
--------------------------------------------------------
将1到9 这九个数字分成三个3位数,分求第一个3位数,正好是第二个3位数的二倍,是第三个3位数的三倍。问应当怎样分法。
*问题分析与算法设计
问题中的三个数之间是有数学关系的,实际上只要确定第一个三位数就可以解决问题。
试探第一个三位数之后,计算出另外两个数,将其分别分解成三位数字,进行判断后确定所试探的数是否就是答案。
需要提醒的是:试探的初值可以是123,最大值是333。因为不可能超出该范围。
*程序与程序设计
#include<stdio.h>
int ok(int t,int *z);
int a[9];
void main()
{
int m,count=0;
for(m=123;m<=333;m++)
if(ok(m,a)&&ok(2*m,a+3)&&ok(3*m,a+6))
printf("No.%d: %d %d %d\\n",++count,m,2*m,3*m);
}
int ok(int t,int *z)
{
int *p1,*p2;
for(p1=z;p1<z+3;p1++)
{
*p1=t%10;
t/=10;
for(p2=a;p2<p1;p2++)
if(*p1==0||*p2==*p1)return 0;
}
return 1;
}
*运行结果
No.1:192 384 576
No.2:219 438 657
No.3:273 546 819
No.4:327 654 981
*思考题
求出所有可能的以下形式的算式,每个算式中有九个数位,正好用尽1到9这九个数字。
1)○○○+○○○=○○○ (共有168种可能的组合)
2)○×○○○○=○○○○ (共有2种可能的组合)
3)○○×○○○=○○○○ (共有7种可能的组合)
4)○×○○○=○○×○○○ (共有13种可能的组合)
5)○×○○○=○×○○○○ (共有28种可能的组合)
6)○○×○○=○×○○○○ (共有7种可能的组合)
7)○○×○○=○○×○○○ (共有11种可能的组合)
--------------------------------------------------------------------------------
将1、2、3、4、5、6、7、8、9九个数字分成三组,每个数字只能用一次,即每组三个数不允许有重复数字,也不许同其它组的三个数字重复,要求每组中的三位数都组成一个平方数。
*问题分析与算法设计
本问题的思路很多,这里介绍一种简单快速的算法。
首先求出三位数中不包含0且是某个整数平方的三位数,这样的三位数是不多的。然后将满足条件的三位数进行组合,使得所选出的3个三位数的9个数字没有重复。
程序中可以将寻找足条件的三位数的过程和对该三位数进行数字分解的过程结合起来。
*程序与程序注释
#include<stdio.h>
void main()
{
int a[20],num[20][3],b[10];
int i,j,k,m,n,t,flag;
printf("The 3 squares with 3 different digits each are:\\n");
for(j=0,i=11;i<=31;i++)
if(i%10!=0)
{
k=i*i;
num[j+1][0]=k/100;
num[j+1][1]=k/10%10;
num[j+1][2]=k%10;
if(!(num[j+1][0]==num[j+1][1]||num[j+1][0]==num[j+1][2]||
num[j+1][1]==num[j+1][2]))
a[++j]=k;
}
for(i=1;i<=j-2;++i)
{
b[1]=num[0];
b[2]=num[1];
b[3]=num[2];
for(t=i+1;t<=j-1;++t)
{
b[4]=num[t][0];
b[5]=num[t][1];
b[6]=num[t][2];
for(flag=0,m=1;!flag&&m<=3;m++)
for(n=4;!flag&&n<=6;n++)
if(b[m]==b[n])flag=1;
if(!flag)
for(k=t+1;k<=j;k++)
{
b[7]=num[k][0];
b[8]=num[k][1];
b[9]=num[k][2];
for(flag=0,m=1;!flag&&m<=6;m++)
for(n=7;!flag&&n<=9;n++)
if(b[m]==b[n])flag=1;
if(!flag)
printf("%d,%d,%d\\n",a,a[t],a[k]);
}
}
}
}
*运行结果
The 3 squares with 3 different digits each are:
361,529,784
*思考题
将1、2、3、4、5、6、7、8、9九个数字分成二组,每个数字只能用一次,一组形成一个5位数,另一组形成一个4位数,使得前者为后者 的n倍。求所有满足条件的5位数和4位数。(注意:N的最大值等于68,68以内的某些N也是不可能的。不可能的N值包括:1、10、11、20、21、 25、30、31等共32个。)
-------------------------------------------------------
任意给出8个整数,将这8个整数分别放在一个立方体的八个顶点上,要求每个面上的四个数之和相等。
*问题分析与算法设计
简化问题:将8个顶点对应数组中的8个元素,将“每个面上的四个数之和皆相等”转换为数组无素之间和的相等关系。这里的关键在于正确地将立方体的8个顶点与数组的8个元素对应。
可以利用简单的穷举方法建立8个数的全部排列。
*程序与程序注释
#include<stdio.h>
#include<stdlib.h>
void main()
{
int a[9],ii=0,i,a1,a2,a3,a4,b1,b2,b3,b4,flag;
for(i=1;i<=8;i++)
{
printf("Please enter [%d]number:",i);
scanf("%d",&a);
ii+=a;
}
printf("******************************************\\n");
if(ii%2)
{
printf("Sorry they can\'t be constructed required cube!\\n");
exit(0);
}
for(flag=0,a1=1;a1<=8;a1++)
for(a2=1;a2<=8;a2++)
if(a2!=a1)
for(a3=1;a3<=8;a3++)
if(a3!=a2&&a3!=a1)
for(a4=1;a4<=8;a4++)
if(a4!=a3&&a4!=a2&&a4!=a1)
for(b1=1;b1<=8;b1++)
if(b1!=a4&&b1!=a3&&b1!=a2&&b1!=a1)
for(b2=1;b2<=8;b2++)
if(b2!=b1&&b2!=a4&&b2!=a3&&b2!=a2&&b2!=a1)
for(b3=1;b3<=8;b3++)
if(b3!=b2&&b3!=b1&&b3!=a4&&b3!=a3&&b3!=a2&&b3!=a1)
for(b4=1;b4<=8;b4++)
if(b4!=b2&&b4!=b1&&b4!=b3&&b4!=a4&&b4!=a3&&b4!=a2&&b4!=a1)
if(a[b1]+a[b2]+a[b3]+a[b4]==ii/2
&&a[a1]+a[a2]+a[b1]+a[b2]==ii/2
&&a[a1]+a[a4]+a[b1]+a[b4]==ii/2)
{
flag=1;goto out;
}
out:
if(flag)
{
printf("They can be constructed required cube as follow:\\n");
printf(" /%2d............/%2d\\n",a[a4],a[a3]);
printf(" %2d/............%2d/|\\n",a[a1],a[a2]);
printf(" | | | |\\n");
printf(" | | | |\\n");
printf(" | %2d| | |%2d\\n",a[b4],a[b3]);
printf(" /................/\\n");
printf(" %2d/.............%2d/\\n",a[b1],a[b2]);
}
else printf("Sorry they can\'t be constructed required cube!\\n");
--------------------------------------------------------------------------------
编写程序求解下式中各字母所代表的数字,不同的字母代表不同的数字。
PEAR
- ARA
--------
PEA
*问题分析与算法设计
类似的问题从计算机算法的角度来说是比较简单的,可以采用最常见的穷举方法解决。程序中采用循环穷举每个字母所可能代表的数字,然后将字母代表的数字转换为相应的整数,代入算式后验证算式是否成立即可解决问题。
*程序与程序注释
#include<stdio.h>
void main()
{
int p,e,a,r;
for(p=1;p<=9;p++)
for(e=0;e<=9;e++)
if(p!=e)
for(a=1;a<=9;a++)
if(a!=p&&a!=e)
for(r=0;r<=9;r++)
if(r!=p&&r!=e&&r!=a&&p*1000+e*100+a*10+r-(a*100+r*10+a)
==p*100+e*10+a)
{
printf(" PEAR %d%d%d%d\\n",p,e,a,r);
printf(" -ARA - %d%d%d\\n",a,r,a);
printf(".........................\\n");
printf(" PEA %d%d%d\\n",p,e,a);
}
}
*运行结果
PEAR 1098
- ARA - 989
---------- ------
PEA 109
*思考题
请复原下面的和式。不同的字母代表不同的数字。
SEVEN 82524 82526
THREE 19722 19722
+ TWO 答案: + 106 + 104
---------- ----------- -----------
TWELVE 102352 102352
-----------------------------------------------------------
A代表数字0到9中的前五个数字,Z代表后五个数字,请还原下列乘式。
A Z A
× A A Z
------------
A A A A
A A Z Z
Z A A
------------
Z A Z A A
*问题分析与算法设计
问题本身并不复杂,可以对乘式中的每一位使用穷举法,最终可以得到结果。本题的关键在于怎样有效的判断每个部分积的每一位是否满足题意,这一问题处理不好,编写的程序会很长。程序实现中采用了一个判断函数,通过传入函数的标志字符串对所有的数进行统一的判断处理。
*程序与程序注释
#include<stdio.h>
void print(long a,long b,long s1,long s2,long s3);
int jud(long q,char *pflag);
void main()
{
long i,j,k,l,m,n,term,t1,t2,t3;
int flag;
for(i=0;i<=4;++i)
for(j=5;j<=9;++j)
for(k=0;k<=4;++k)
{
term=100*i+10*j+k;
for(flag=0,n=0;n<4&&!flag;)
flag=jud((t3=++n*100*term)/100,"001");
if(flag)
{
for(flag=0,m=0;m<4&&!flag;)
flag=jud((t2=++m*10*term)/10,"1100");
if(flag)
{
for(flag=0,l=5;l<9&&!flag;)
flag=jud(t1=++l*term,"0000");
if(flag&&jud(t1+t2+t3,"00101"))
print(term,n*100+m*10+l,t1,t2,t3);
}
}
}
}
void print(long a,long b,long s1,long s2,long s3)
{
printf("\\n %ld\\n",a);
printf("*) %ld\\n",b);
printf("......................\\n");
printf(" %ld\\n %ld\\n %ld\\n",s1,s2/10,s3/100);
printf("......................\\n");
printf(" %ld\\n",a*b);
}
int jud(long q,char *pflag)
{
while(q!=0&&*pflag!=NULL)
if(*pflag-\'0\'!=(q%10>=5?1:0))
return 0;
else
{
q/=10;++pflag;
}
if(q==0&&*pflag==NULL)
return 1;
else return 0;
}
*运行结果
3 7 2
× 2 4 6
----------
2 2 3 2
1 4 8 8
7 4 4
------------
9 1 5 1 2
*思考题
E代表数字0到9中的偶数数字,O代表奇数数字,请还原下列乘式。
E E O 2 8 5
× O O 答案 × 3 9
----------- -----------
E O E O 2 5 6 5
E O O 8 5 5
----------- -----------
O O O O O 1 1 1 1 5
有乘法算式如下:
○○○
× ○○
------------
○○○○
○○○○
------------
○○○○○
18个○的位置上全部是素数(1、3、5或7),请还原此算式。
*问题分析与算法设计
问题中虽然有18数位,但只要确定乘数和被乘数后经过计算就可确定其它的数位。
乘数和被乘数共有5个数位,要求每个数都是质数。完全可以采用穷举的方法对乘数和被乘数进行穷举,经过判断后找出答案。但是这种方法给人的 感觉是“太笨了”,因为组成的数字只是质数(4个),完全没有必要在那么大的范围内进行穷举,只需要试探每一位数字为质数时的情况即可。
采用五重循环的方法实现对于5个数字的穷举,前面的许多例题中都已见过。循环实现简单易行,但嵌套的层次太多,需要穷举的变量的数量直接影 响到循环嵌套的层数,这种简单的实现方法缺少技巧性。本例的程序中给出了另外一种同样功能的算法,该算法的实现思想请阅读程序。
程序中并没有直接对质数进行穷举,而是将每个质数与1到4顺序一一对应,在穷举时为处理简单仅对1到4进行穷举处理,待要判断产生的乘积是否满足条件时再利用一个数组完成向对应质数的转换。请体会程序中的处理方法。程序中使用的算法实际上是回朔法。
*程序与程序注释
#include<stdio.h>
#define NUM 5
#define C_NUM 4
int a[NUM+1];
int b[]={0,2,3,5,7};
int f(long sum);
void main()
{
int i,not_finish=1;
i=2;
a[1]=1;
while(not_finish)
{
while(not_finish&&i<=NUM)
if(a>=C_NUM)
if(i==1&&a[1]==C_NUM)
not_finish=0;
else a[i--]=0;
else a[i++]++;
if(not_finish)
{
long int sum1,sum2,sum3,sum4;
sum1=b[a[1>*100+b[a[2>*10+b[a[3>;
sum2=sum1*b[a[5>;
sum3=sum1*b[a[4>;
if(sum2>=2222&&sum2<=7777&&f(sum2)&&sum3>=2222&&sum3<=7777&&f(sum3))
if((sum4=sum2+sum3*10)>=22222&&sum4<=77777&&f(sum4))
{
printf(" %d\\n",sum1);
printf("* %d%d\\n",b[a[4>,b[a[5>);
printf("........................\\n");
printf(" %d\\n",sum2);
printf(" %d\\n",sum3);
printf("........................\\n");
printf(" %d\\n",sum4);
}
i=NUM;
}
}
}
int f(long sum)
{
int i,k,flag;
while(sum>0)
{
i=sum%10;
for(flag=0,k=1;!flag&&k<=C_NUM;k++)
if(b[k]==i)
{
flag=1;break;
}
if(!flag) return 0;
else sum=sum/10;
}
return 1;
}
*运行结果
7 7 5
× 3 3
----------
2 3 2 5
2 3 2 5
-----------
2 5 5 7 5
*思考题
以下乘式中,A、B、C代表一确定的数字,○代表任意数字,请复原。
A B C 2 8 6
× B A C × 8 2 6
------------- 答案: ------------
○○○○ 1 7 1 6
○○A 5 7 2
○○○B 2 2 8 8
------------- ----------------
○○○○○○ 2 3 6 2 3 6
--------------------------------------------------------------------------------
给定下列除式,其中包含5个7,其它打×的是任意数字,请加以还原。
× 7 × --------------商
--------------
除数------××| ×××××-------------被除数
×7 7
--------------
× 7 ×
× 7 ×
----------
× ×
× ×
----------
○
*题目分析与算法设计
首先分析题目,由除式本身尽可能多地推出已知条件。由除式本身书已知:
1、被除数的范围是10000到99999,除数的范围是10到99,且可以整除;
2、商为100到999之间,且十位数字为7;
3、商的第一位与除数的积为三位数,且后两位为77;
4、被除数的第三位一定为4;
5、 7乘以除数的积为一个三位数,且第二位为7;
6、商的最后一位不能为0,且与除数的积为一个二位数。
由已知条件就可以采用穷举的方法找出结果。
*程序与程序注释
#include<stdio.h>
void main()
{
long int i;
int j,l;
for(i=10000;i<=99999;i++)
if(i%1000-i%100==400)
for(j=10;j<=99;j++)
if(i%j==0&&(l=i/j)%100>=70&&l%100<80&&l%10!=0&&l>100&&l<=999)
if((j*(l%10))<100&&j*(l%10)>10)
if(j*7%100>=70&&j*7%100<80)
if(j*(l/100)%100==77&&j*(l/100)>100)
printf("%ld/%ld=%d\\n",i,j,l);
}
*运行结果
51463/53=971。
可以看作为下列算式:
9 7 1
-------------
5 3| 5 1 4 6 3
4 7 7
-------------
3 7 6
3 7 1
-----------
5 3
5 3
-----------
○
*问题的进一步讨论
在推出的已知条件中,几所有的条件都是十分明显的,换句话说,推出的已知条件就是对题目的平铺直叙。这种推已知条件的方法十分简单,并且行之有效。
*思考题
下列除式中仅给定了一个8,其它打×的位置上是任意数字,请还原。
× 8 × ----------------商
----------------
除数-------×××| ××××××---------------被除数
××××
---------------
×××
×××
---------------
××××
××××
---------------
○
--------------------------------------------------------
下列除式中仅在商中给定了一个7,其它打×的位置全部是任意数字,请还原。
×7××× -------------商
------------------
除数 -------------------×××| ×××××××× -------------被除数
×××× -------------1)
---------------
××× -------------2)
××× -------------3)
---------------
×××× -------------4)
××× -------------5)
-----------------
×××× -------------6)
×××× -------------7)
-----------------
0
*题目分析与算法设计
这道题是不可能用单纯的穷举法求解的,一则计算时间太长,二则难于求出除式中各部分的值。
对除式进行分析,改可能多地推出限制条件:
由3)可以看出,商的第二位7乘除数得一个三位数,所以除数<=142。
由除数乘商的第一位为一个四位数可知,商的第一位只能为8或9且除数>=112。同时商的第五位也为8或9数的前四位一定<=142*9+99且>=1000+10。
由4)、5)、6)可以看出,4)的前两位一定为“10”;5)的第一位一定为“9”;6)的前两位一定在10到99之间;商的第四位一定为为0。
由 5)的第一位一定是“9”和“112”<=除数<=142可知:商的第三位可能为7或8。
由除式本身可知:商的第四位为0。
由 1)可知:除数X商的第一位应当为一个四位数。
由 5)可知:除数X商的第三位应当为一个三位数。
编程时为了方便,将被除数分解:前四位用a[0]表示,第五位用a[1],第六位用a[2],第七八两位用a[3];除数用变量b表示;分 解商:第一位用c[0],第五位用c[2];其它的部分商分别表示为:2)的前两位为d[0],4)的前三位为d[1],6)的前二位为d[2]。将上述 分析用数学的方法综合起来可以表示为:
被除数: 1010<=a[0]<=1377 0<=a[1]<=9
0<=a[2]<=9 0<=a[3]<=99
除数: 112<=b <=142
商: 8<=c[0]<=9 7<=c[1]<=8 8<=c[2]<=9
2)的前两位: 10<=d[0]<=99
4)的前三位: 100<=d[1]<b
6)的前两位: 10<=d[2]<=99
1)式部分积: b*c[0]>1000
5)式部分积: 100<b*c[1]<1000
*程序与程序注释
#include<stdio.h>
void main()
{
int a[4],b,c[3],d[4],i=1;
for(a[0]=1010;a[0]<=1377;a[0]++)
for(b=112;b<=142;b++)
for(c[0]=8;c[0]<=9;c[0]++)
if(b*c[0]>1000&&(d[0]=a[0]-b*c[0])>=10&&d[0]<100)
for(a[1]=0;a[1]<=9;a[1]++)
if((d[1]=d[0]*10+a[1]-b*7)>=100&&d[1]<b)
for(a[2]=0;a[2]<=9;a[2]++)
for(c[1]=7;c[1]<=8;c[1]++)
if(b*c[1]<1000&&(d[2]=d[1]*10+a[2]-b*c[1])>=10&&d[2]<100)
for(a[3]=0;a[3]<=99;a[3]++)
for(c[2]=8;c[2]<=9;c[2]++)
if(d[2]*100+a[3]-b*c[2]==0)
{
printf("No%2d:",i++);
printf("%d%d%d%d%d/",a[0],a[1],a[2],a[3]/10,a[3]%10);
printf("%d=",b);
printf("%d%d%d%d%d\\n",c[0],7,c[1],0,c[2]);
}
}
*运行结果:
No 1:12128316/124=97809
*思考题
下列除式中“×”所在的位置全部是任意数字,请还原。
×××××
-------------------
××× | ××××××××
××××
------------------
××××
×××
---------------
×××
×××
-----------
××××
××××
-----------
0
--------------------------------------------------------------------------------
求九位累进可除数。所谓九位累进可除数就是这样一个数:这个数用到1到9这九个数字组成,每个数字刚好只出现一次。这九个位数的前两位能被2整除,前三位能被3整除......前N位能被N整除,整个九位数能被9整除。
*问题分析与算法设计
问题本身可以简化为一个穷举问题:只要穷举每位数字的各种可能取值,按照题目的要求对穷举的结果进行判断就一定可以得到正确的结果。
问题中给出了“累进可除”这一条件,就使得我们可以在穷举法中加入条件判断。在穷举的过程中,当确定部分位的值后,马上就判断产生的该部分 是否符合“累进可除”条件,若符合,则继续穷举下一位数字;否则刚刚产生的那一位数字就是错误的。这样将条件判断引入到穷举法之中,可以尽可能早的发现矛 盾,尽早地放弃不必要穷举的值,从而提高程序的执行效率。
为了达到早期发现矛盾的目的,不能采用多重循环的方法实行穷举,那样编出的程序质量较差。程序中使用的算法不再是穷举法,而是回朔法。
*程序与程序注释
#include<stdio.h>
#define NUM 9
int a[NUM+1];
void main()
{
int i,k,flag,not_finish=1;
long sum;
i=1;
a[1]=1;
while(not_finish)
{
while(not_finish&&i<=NUM)
{
for(flag=1,k=1;flag&&k<i;k++)
if(a[k]==a)flag=0;
for(sum=0,k=1;flag&&k<=i;k++)
{
sum=10*sum+a[k];
if(sum%k)flag=0;
}
if(!flag)
{
if(a==a[i-1])
{
i--;
if(i>1&&a==NUM)
a=1;
else if(i==1&&a==NUM)
not_finish=0;
else a++;
}
else if(a==NUM) a=1;
else a++;
}
else
if(++i<=NUM)
if(a[i-1]==NUM) a=1;
else a=a[i-1]+1;
}
if(not_finish)
{
printf("\\nThe progressire divisiable number is:");
for(k=1;k<=NUM;k++)
printf("%d",a[k]);
if(a[NUM-1]<NUM) a[NUM-1]++;
else a[NUM-1]=1;
not_finish=0;
printf("\\n");
}
}
}
*运行结果
The progressire divisible number is: 381654729
*思考题
求N位累进可除数。用1到9这九个数字组成一个N(3<=N<=9)位数,位数字的组成不限,使得该N位数的前两位能被2整除,前3位能被3整除,......,前N位能被N整除。求满足条件的N位数。
----------------------------------------------------------------------
魔术师利用一副牌中的13张黑桃,预先将它们排好后迭在一起,牌面朝下。对观众说:我不看牌,只数数就可以猜到每张牌是什么,我大声数数, 你们听,不信?你们就看。魔术师将最上面的那张牌数为1,把它翻过来正好是黑桃A,将黑桃A放在桌子上,然后按顺序从上到下数手上的余牌,第二次数1、 2,将第一张牌放在这迭牌的下面,将第二张牌翻过来,正好是黑桃2,也将它放在桌子上,第三次数1、2、3,将前面两张依次放在这迭牌的下面,再翻第三张 牌正好是黑桃3。这样依次进行将13张牌全翻出来,准确无误。问魔术师手中的牌原始顺序是怎样安排的?
*问题分析与算法设计
题目已经将魔术师出牌的过程描述清楚,我们可以利用倒推的方法,很容易地推出原来牌的顺序。
人工倒推的方法是:在桌子上放13空盒子排成一圈,从1开始顺序编号,将黑桃A放入1号盒子中,从下一个空盒子开始对空的盒子计数,当数到 第二个空盒子时,将黑桃2放入空盒子中,然后再从下一个空盒子开始对空盒子计数,顺序放入3、4、5...,直到放入全部3张牌。注意在计数时要跳过非空 的盒子,只对空盒子计数。最后牌在盒子中的顺序,就是魔术师手中原来牌的顺序。
这种人工的方法是行之有效的,计算机可以模拟求解。
*程序与程序注释
#include<stdio.h>
int a[14];
void main()
{
int i,n,j=1;
printf("The original order of cards is:");
for(i=1;i<=13;i++)
{
n=1;
do{
if(j>13) j=1;
if(a[j]) j++;
else{ if(n==i) a[j]=i;
j++;n++;
}
}while(n<=i);
}
for(i=1;i<=13;i++)
printf("%d ",a);
printf("\\n");
}
*运行结果
The original order of cards is:1 8 2 5 10 3 12 11 9 4 7 6 13
-----------------------------------------------------------------
魔术师再次表演,他将红桃和黑桃全部迭在一起,牌面朝下放在手中,对观众说:最上面一张是黑桃A,翻开后放在桌上。以后,从上至下每数两张 全依次放在最底下,第三张给观众看,便是黑桃2,放在桌上后再数两张依次放在最底下,第三张给观众看,是黑桃3。如此下去,观众看到放在桌子上牌的顺序 是:
黑桃 A 2 3 4 5 6 7 8 9 10 J Q K
红桃 A 2 3 4 5 6 7 8 9 10 J Q K
问魔术师手中牌的原始顺序是什么?
*问题分析与算法设计
本题可在上题的基础上进行编程,不同的在于计数的方法和牌的张数,这些并不影响我们求解题目的思路,仍可按照倒推的方法,得到原来魔术师手中的牌的顺序。
*程序与程序注释
#include<stdio.h>
int a[27];
void main()
{
int i,n,j=1;
a[1]=1;
printf("The original order of cards is:(r:rad b:block):\\n");
for(i=2;i<=26;i++)
{
n=1;
do{
if(j>26) j=1;
if(a[j]) j++;
else{
if(n==3) a[j]=i;
j++; n++;
}
}while(n<=3);
}
for(i=1;i<=26;i++)
{
printf("%c",a>13? \'r\':\'b\');
printf("%d ",a>13? a-13:a);
if(i==13) printf("\\n");
}
printf("\\n");
}
*运行结果
The original order of cards is:(r:rad b:black):
b1 r6 b10 b2 r12 r3 b3 b11 r9 b4 r7 b12 b5
r4 r13 b6 b13 r11 b7 r5 r1 b8 r8 r10 b9 r2
这是17世纪的法国数学家加斯帕在《数目的游戏问题》中讲的一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人 就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
*问题分析与算法设计
约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
题目中30个人围成一圈,因而启发我们用一个循环的链来表示。可以使用结构数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该 人是否被扔下海的标记,为1表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为0,表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。
*程序与程序注释
#include<stdio.h>
struct node
{
int nextp;
int no_out;
}link[31];
void main()
{
int i,j,k;
printf("The original circle is(+:pagendom,@:christian):\\n");
for(i=1;i<=30;i++)
{
link.nextp=i+1;
link.no_out=1;
}
link[30].nextp=1;
j=30;
for(i=0;i<15;i++)
{
for(k=0;;)
if(k<15)
{
j=link[j].nextp;
k+=link[j].no_out;
}
else break;
link[j].no_out=0;
}
for(i=1;i<=30;i++)
printf("%c",link.no_out? \'@\':\'+\');
printf("\\n");
}
*运行结果
The original circle is(+:pagandom, @:christian):
+++@@+@+@@@+@+++@@+@@@+++@+@@+
(+"表示被扔下海海的非教徒 @:留在船上活命的教徒)
*思考题
有N个小孩围 成一圈并依次编号,教师指定从第M个小孩开始报数,报到第S个小孩即令其出列。然后从下一个孩子继续报数,数到第S个小孩又令其出列,如此直到所有的孩子都出列。求小孩出列的先后顺序。
--------------------------------------------------------------------------------
某人有四张3分的邮票和三张5分的邮票,用这些邮票中的一张或若干张可以得到多少种不同的邮资?
*问题分析与算法设计
将问题进行数学分析,不同张数和面值的邮票组成的邮资可用下列公式计算:
S=3*i+5*j
其中i为3分邮柰的张数,j为5分的张数
按题目的要求,3分的邮票可以取0、1、2、3、4张,5分的邮票可以取0、1、2、3张。采用穷举方法进行组合,可以求出这些不同面值不同张数的邮标组合后的邮资。
*程序与程序注释
#include<stdio.h>
int a[27];
void main()
{
int i,j,k,s,n=0;
for(i=0;i<=4;i++)
for(j=0;j<=3;j++)
{
s=i*3+j*5;
for(k=0;a[k];k++)
if(s==a[k])break;
if(!a[k]&&s)
{
a[k]=s; n++;
}
}
printf("%d kinds:",n);
for(k=0;a[k];k++)
printf("%d ",a[k]);
printf("\\n");
}
*运行结果
19 kinds: 5 10 15 3 8 13 18 6 11 16 21 9 14 19 24 12 17 22 27
-------------------------------------------------------------
已知五个互不相同的正整数之和为23,且从这五个数中挑选若干个加起来可以表示从1到23之内的全部自然数。问这五个数是什么?
*问题分析与算法设计
从计算机程序设计的角度来说,可以用穷举法分解23,然后判断所分解的五个数是否可以表示1到23 之间的全部整数。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,c,d,e,i,j,k,l,m,x,count=0,f=0;
printf("There are following possble result:\\n");
for(a=1;a<=23;a++)
for(b=1+a;b<=23-a;b++)
for(c=1+b;c<=23-a-b;c++)
for(d=1+c;d<=23-a-b-c;d++)
{
f=1;
if((e=23-a-b-c-d)>d)
for(f=0,x=1;x<24&&!f;x++)
for(f=1,i=0;i<2&&f;i++)
for(j=0;j<2&&f;j++)
for(k=0;k<2&&f;k++)
for(l=0;l<2&&f;l++)
for(m=0;m<2&&f;m++)
if(x==a*i+b*j+c*k+d*l+e*m) f=0;
if(!f) printf("[%d]: %d %d %d %d %d\\n",++count,a,b,c,d,e);
}
}
*运行结果
There are following possble result:
[1]: 1 2 3 5 12
[2]: 1 2 3 6 11
[3]: 1 2 3 7 10
[4]: 1 2 4 5 11
[5]: 1 2 4 6 10
[6]: 1 2 4 7 9
法国数学家梅齐亚克在他著名的《数字组合游戏》(1962)中提出了一个问题:一位商人有一个重40磅的砝码,一天不小心将砝码摔成了四块。后来商人称得每块的重量都是整磅数,而且发现这四块碎片可以在天平上称1至40磅之间的任意重量。请问这四块碎片各重多少?
*问题分析与算法设计
本题是上一题的发展。题目中给出的条件是“在天平上”,这意味着:同一砝码既可以放在天平的左侧,也可以放在天平的右侧。若规定重物只能放在天平的左侧,则当天平平衡时有:
重物重量+左侧砝码重量总和=右侧砝码重量总和
由此可得:
重物重量=右侧砝码重量总和-左侧砝码重量总和
编程时只要根据以上公式,使“右侧砝码重量总和-左侧砝码重量总和”可以表示1到40之间的全部重量即可。编程中要注意的是:怎样采用一种简单的方法来表示一个砝码是在天平的左侧还是在天平的右侧,或是根本没有使用。
以下程序采用1、 -1和0分别表示上述三种情况,请注意理解。
*程序与程序注释
#include<stdio.h>
#include<math.h>
void main()
{
int weight1,weight2,weight3,weight4,d1,d2,d3,d4,x,flag;
printf("The weight is broke up as following 4 pieces:");
for(weight1=1;weight1<=40;weight1++)
for(weight2=weight1+1;weight2<=40-weight1;weight2++)
for(weight3=weight2+1;weight3<=40-weight1-weight2;weight3++)
if((weight4=40-weight1-weight2-weight3)>=weight3)
{
for(flag=1,x=1;x<41&&flag;x++)
for(flag=0,d1=1;d1>-2;d1--)
for(d2=1;d2>-2&&!flag;d2--)
for(d3=1;d3>-2&&!flag;d3--)
for(d4=1;d4>-2&&!flag;d4--)
if(x==weight1*d1+weight2*d2+weight3*d3+weight4*d4)
flag=1;
if(flag) printf("%d %d %d %d\\n",weight1,weight2,weight3,weight4);
flag=0;
}
}
*运行结果
The weight is broke up as following 4 pieces: 1 3 9 27
----------------------------------------------------------
十个小孩围成一圈分糖果,老师分给第一个小孩10块,第二个小孩2块,第三个小孩8块,第四个小孩22块,第五个小孩16块,第六个小孩4 块,第七个小孩10块,第八个小孩6块,第九个小孩14块,第十个小孩20块。然后所有的小孩同时将手中的糖分一半给右边的小孩;糖块数为奇数的人可向老 师要一块。问经过这样几次后大家手中的糖的块数一样多?每人各有多少块糖?
*问题分析与算法设计
题目描述的分糖过程是一个机械的重复过程,编程算法完全可以按照描述的过程进行模拟。
*程序与程序注释
#include<stdio.h>
void print(int s[]);
int judge(int c[]);
int j=0;
void main()
{
static int sweet[10]={10,2,8,22,16,4,10,6,14,20};
int i,t[10],l;
printf(" child\\n");
printf(" round 1 2 3 4 5 6 7 8 9 10\\n");
printf(".............................\\n");
print(sweet);
while(judge(sweet))
{
for(i=0;i<10;i++)
if(sweet%2==0)
t=sweet=sweet/2;
else
t=sweet=(sweet+1)/2;
for(l=0;l<9;l++)
sweet[l+1]=sweet[l+1]+t[l];
sweet[0]+=t[9];
print(sweet);
}
}
int judge(int c[])
{
int i;
for(i=0;i<10;i++)
if(c[0]!=c) return 1;
return 0;
}
void print(int s[])
{
int k;
printf(" %2d ",j++);
for(k=0;k<10;k++) printf("%4d",s[k]);
printf("\\n");
}
--------------------------------------------------------------------------------
小明假期同爸爸一起去书店,他选中了六本书,每本书的单价分别为:3.1,1.7,2,5.3,0.9和7.2。不巧的是,小明的爸爸只带了 十几块钱,为了让小明过一个愉快的假期,爸爸扔然同意买书,但提邮购一个要求,要小明从六本书中选出若干本,使得单价相加所得的和同10最接近。你能够帮 助小明解决这个问题吗?
*问题分析与算法设计
分析题意,可将题目简化为:从六个数中选出若干个求和,使得和与10的差值最小。
题目中隐含两个问题,其一是怎样从六个数中选出若干个数;其二是求与10的差。
从六个数中选出若干个数实质是从六个数中选出若干个进行组合。每个数在组合过程中只有两种情况:要么是选中参加求和,要么是没选中不参加求和。这样就可以使用六重循环对每个数是否参加求和进行全部可能情况的组合。
关于求与10的差值应当注意的是:差值的含义是指差的绝对值。例如:“9-10=-1"和"11-10=1",但9和11这两者与10的差值都是1。若认为”9“与”10的差值为-1就错了。
*程序与程序注释
#include<stdio.h>
#include<math.h>
void main()
{
int d[6],m,i,j;
long b[63],flag;
float c[6],min,x;
printf("Please enter the prices of 6 books:");
for(i=0;i<6;i++) scanf("%f",&c);
for(i=0,min=-1,d[0]=0;d[0]<2;d[0]++)
for(d[1]=0;d[1]<2;d[1]++)
for(d[2]=0;d[2]<2;d[2]++)
for(d[3]=0;d[3]<2;d[3]++)
for(d[4]=0;d[4]<2;d[4]++)
for(d[5]=0;d[5]<2;d[5]++)
{
for(flag=0,x=0,j=5;j>=0;j--)
{
x+=c[j]*d[j]; flag=flag*10+d[j];
}
x=((x-10>0)? x-10:10-x);
if(min<0)
{
min=x;
b[i++]=flag;
}
else if(min-x>1.e-6)
{
min=x; b[0]=flag; i=1;
}
else if(fabs((double)x-min)<1.e-6)
b[i++]=flag;
}
for(m=0;m<i;m++)
{
printf("10(+ -)%.2f=",min);
for(flag=b[m],j=0;flag>0;j++,flag/=10)
if(flag%10)
if(flag>1) printf("%.2f+",c[j]);
else printf("%.2f\\n",c[j]);
}
}
*运行结果
Please enter the prices of 6 books:3.1 1.7 2.0 5.3 0.9 7.2
10(+ -)0.10=2.00+0.90+7.20
10(+ -)0.10=1.70+2.00+5.30+0.90
10(+ -)0.10=3.10+1.70+5.30
*思考题
可以看出,程序中求六个数所能产生全部组合的算法并不好,使用六重循环进行处理使程序显得不够简洁。可以设计出更通用、优化的算法产生全部组合。
法国著名数学家波瓦松在表年时代研究过一个有趣的数学问题:某人有12品脱的啤酒一瓶,想从中倒出6品脱,但他没有6品脱的容器,仅有一个8品脱和5品脱的容器,怎样倒才能将啤酒分为两个6品脱呢?
*问题分析与算法设计
将12品脱酒 8品脱和5品脱的空瓶平分,可以抽象为解不定方程:
8x-5y=6
其意义是:从12品脱的瓶中向8品脱的瓶中倒x次,并且将5品脱瓶中的酒向12品脱的瓶中倒y次,最后在12品脱的瓶中剩余6品脱的酒。
用a,b,c代表12品脱、8品脱和5品脱的瓶子,求出不定方程的整数解,按照不定方程的意义则倒法为:
a -> b -> c ->a
x y
倒酒的规则如下:
1) 按a -> b -> c ->a的顺序;
2) b倒空后才能从a中取
3) c装满后才能向a中倒
按以上规则可以编写出程序如下:
*程序与程序注释
#include<stdio.h>
void getti(int a,int y,int z);
int i;
void main()
{
int a,y,z;
printf("input Full a,Empty b,c,Get i:");
scanf("%d%d%d%d",&a,&y,&z,&i);
getti(a,y,z);
getti(a,z,y);
}
void getti(int a,int y,int z)
{
int b=0,c=0;
printf(" a%d b%d c%d\\n %4d%4d%4d\\n",a,y,z,a,b,c);
while(a!=i||b!=i&&c!=i)
{
if(!b)
{ a-=y; b=y;}
else if(c==z)
{ a+=z; c=0;}
else if(b>z-c)
{ b-=(z-c);c=z;}
else{ c+=b; b=0;}
printf(" %4d %4d %4d\\n",a,b,c);
}
}
请利用“正多边形逼近”的方法求出π的近似值
*问题分析与算法设计
利用“正多边形逼近”的方法求出π值在很早以前就存在,我们的先人祖冲之就是用这种方法在世界上第一个得到精确度达小数点后第6位的π值的。
利用圆内接正六边形边长等于半径的特点将边数翻番,作出正十二边形,求出边长,重复这一过程,就可获得所需精度的π的近似值。
假设单位圆内接多边形的边长为2b,边数为i,则边数加倍后新的正多边形的边长为:
周长为:
y=2 * i * x i:为加倍前的正多边形的边数
*程序与程序注释
#include<stdio.h>
#include<math.h>
void main()
{
double e=0.1,b=0.5,c,d;
long int i;
for(i=6;;i*=2)
{
d=1.0-sqrt(1.0-b*b);
b=0.5*sqrt(b*b+d*d);
if(2*i*b-i*e<1e-15) break;
e=b;
}
printf("pai=%.15lf\\n",2*i*b);
printf("The number of edges of required polygon:%ld\\n",i);
}
*运行结果
pai=3.141592653589794
The number of edges of required polygon:100663296
*思考题
请用外切正多边形逼近的方法求π的近似值。
利用随机数法求π的近似值
*问题分析与算法设计
随机数法求π的近似值的思路:在一个单位边长的正方形中,以边长为半径,以一个顶点为圆心,在政权方形上作四分之一圆。随机的向正方形内扔 点,若落入四分之一圆内则计数。重复向正方形内扔足够多的点,将落入四分之一圆内的计数除以总的点数,其值就是π值四分之一的近似值。
按此方法可直接进行编程,注意:本方法求出的π值只有统计次数足够多时才可能准确。
*程序与程序注释
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#define N 30000
void main()
{
float x,y;
int c=0,d=0;
randomize();
while(c++<=N)
{
x=random(101);
y=random(101);
if(x*x+y*y<=10000)
d++;
}
printf(" pi=%f\\n",4. *d/N);
}
*运行结果
多次运行程序,可能得到多个不同的对口果,这是因为采用的是统计规律求出的近似值,只有当统计的次数足够大时,才可能逼近π值。运行四次,可能的结果是:
3.122267
3.139733
3.133733
3.106800
编程验证“大于1000的奇数其平方与1的差是8的倍数”。
*问题分析与算法设计
本题是一个很容易证明的数学定理,我们可以编写程序验证它。
题目中给出的处理过程很清楚,算法不需要特殊设计。可以按照题目的叙述直接进行验证(程序中仅验证到3000)。
*程序与程序注释
#include<stdio.h>
void main()
{
long int a;
for(a=1001;a<=3000;a+=2)
{
printf("%ld:",a);
printf("(%ld*%ld-1)/8",a,a);
printf("=%ld",(a*a-1)/8);
printf("+%ld\\n",(a*a-1)%8);
}
}
日本一位中学生发现一个奇妙的“定理”,请角谷教授证明,而教授无能为力,于是产生角谷猜想。猜想的内容是:任给一个自然数,若为偶数除以2,若为奇数则乘3加1,得到一个新的自然数后按照上面的法则继续演算,若干次后得到的结果必然为1。请编程验证。
*问题分析与算法设计
本题是一个沿未获得一般证明的猜想,但屡试不爽,可以用程序验证。
题目中给出的处理过程很清楚,算法不需特殊设计,可按照题目的叙述直接进行证。
*程序与程序注释
#include<stdio.h>
void main()
{
int n,count=0;
printf("Please enter number:");
scanf("%d",&n);
do{
if(n%2)
{
n=n*3+1;
printf("[%d]:%d*3+1=%d\\n",++count,(n-1)/3,n);
}
else
{
n/=2;
printf("[%d]: %d/2=%d\\n",++count,2*n,n);
}
}while(n!=1);
}
数论中著名的“四方定理”讲的是:所有自然数至多只要用四个数的平方和就可以表示。
请编程证此定理。
*问题分析与算法设计
本题是一个定理,我们不去证明它而是编程序验证。
对四个变量采用试探的方法进行计算,满足要求时输出计算结果。
*程序与程序注释
#include<stdio.h>
#include<stdlib.h>
void main()
{
int number,i,j,k,l;
printf("Please enter a number=");
scanf("%d",&number);
for(i=1;i<number/2;i++)
for(j=0;j<=i;j++)
for(k=0;k<=j;k++)
for(l=0;l<=k;l++)
if(number==i*i+j*j+k*k+l*l)
{
printf(" %d=%d*%d+%d*%d+%d*%d+%d*%d\\n",number,i,i,j,j,k,k,l,l);
exit(0);
}
}
*运行结果
1) Please enter a number = 110
110=7*7+6*6+4*4+3*3
2) Please enter a number = 211
211=8*8+7*7+7*7+7*7
3) Please enter a number = 99
99=7*7+5*5+4*4+3*3
验证卡布列克运算。任意一个四位数,只要它们各个位上的数字是不全相同的,就有这样的规律:
1)将组成该四位数的四个数字由大到小排列,形成由这四个数字构成的最大的四位数;
2)将组成该四位数的四个数字由小到大排列,形成由这四个数字构成的最小的四位数(如果四个数中含有0,则得到的数不足四位);
3)求两个数的差,得到一个新的四位数(高位零保留)。
重复以上过程,最后得到的结果是6174,这个数被称为卡布列克数。
*问题分析与算法设计
题目中给出的处理过程很清楚,算法不需要特殊设计,可按照题目的叙述直接进行验证。
*程序与程序注释
#include<stdio.h>
void vr6174(int);
void parse_sort(int num,int *each);
void max_min(int *each,int *max,int *min);
void parse_sort(int num,int *each);
int count=0;
void main()
{
int n;
printf("Enter a number:");
scanf("%d", &n);
vr6174(n);
}
void vr6174(int num)
{
int each[4],max,min;
if(num!=6174&&num)
{
parse_sort(num,each);
max_min(each,&max,&min);
num=max-min;
printf("[%d]: %d-%d=%d\\n",++count,max,min,num);
vr6174(num);
}
}
void parse_sort(int num,int *each)
{
int i,*j,*k,temp;
for(i=0;i<=4;i++)
{
j=each+3-i;
*j=num%10;
num/=10;
}
for(i=0;i<3;i++)
for(j=each,k=each+1;j<each+3-i;j++,k++)
if(*j>*k) { temp=*j;*j=*k;*k=temp;}
return;
}
void max_min(int *each,int *max,int *min)
{
int *i;
*min=0;
for(i=each;i<each+4;i++)
*min=*min*10+*i;
*max=0;
for(i=each+3;i>=each;i--)
*max=*max*10+*i;
return;
}
*运行结果
1) Enter a number:4312
[1]:4312-1234=3078
[2]:8730-378=8352
[3]:8532-2358=6174
2) Enter a number:8720
[1]:8720-278=8442
[2]:8442-2448=5994
[3]:9954-4599=5355
[4]:5553-3555=1998
[5]:9981-1899=8082
[6]:8820-288=8523
[7]:8532-2358=6174
3)Enter a number:9643
[1]:9643-3469=6174
--------------------------------------------------------------------------------
验证尼科彻斯定理,即:任何一个整数的立方都可以写成一串连续奇数的和。××
*问题分析与算法设计
本题是一个定理,我们先来证明它是成立的。
对于任一正整数a,不论a是奇数还是偶数,整数(a×a-a+1)必然为奇数。
构造一个等差数列,数列的首项为(a×a-a+1),等差数列的差值为2(奇数数列),则前a项的和为:
a×((a×a-a+1))+2×a(a-1)/2
=a×a×a-a×a+a+a×a-a
=a×a×a
定理成立。证毕。
通过定理的证明过程可知L所要求的奇数数列的首项为(a×a-a+1),长度为a。编程的算法不需要特殊设计,可按照定理的证明过直接进行验证。
*程序与程序注释
#include<stdio.h>
void main()
{
int a,b,c,d;
printf("Please enter a number:");
scanf("%d",&a);
b=a*a*a;
printf("%d*%d*%d=%d=",a,a,a,b);
for(d=0,c=0;c<a;c++)
{
d+=a*a-a+1+c*2;
printf(c?"+%d":"%d",a*a-a+1+c*2);
}
if(d==b)printf(" Y\\n");
else printf(" N\\n");
}
*运行结果
1) Please enter a number:13
13*13*13=2197=157+159+161+163+165+167+169+171+173+175+177+179+181 Y
2) Please enter a number:14
14*14*14=2744=183+185+187+189+191+193+195+197+199+201+203+205+207+209 Y
*思考题
本题的求解方法是先证明,在证明的过程中找到编程的算法,然后实现编程。实际上我们也可以不进行证明,直接使用编程中常用的试探方法来找出该数列,验证该定理。请读者自行设计算法。当然这样得到的数列可能与用定理方法得到的数列不一样。
任取一个十进制整数,将其倒过来后与原来的整数相加,得到一个新的整数后重复以上步聚,则最终可得到一个回文数。请编程验证。
*问题分析与算法设计
回文数的这一形成规则目前还属于一个猜想,尚未得到数学上的证明。有些回文数要经历上百个步聚才能获得。这里通过编程验证。
题目中给出的处理过程很清楚,算法不需要特殊设计。可按照题目的叙述直接进行验证。
*程序与程序注释
#include<stdio.h>
#define MAX 2147483647
long re(long int);
int nonres(long int s);
void main()
{
long int n,m;
int count=0;
printf("Please enetr a number optionaly:");
scanf("%ld",&n);
printf("The generation process of palindrome:\\n");
while(!nonres((m=re(n))+n))
{
if(m+n>=MAX)
{
printf(" input error,break.\\n");
break;
}
else
{
printf("[%d]:%ld+%ld=%ld\\n",++count,n,m,m+n);
n+=m;
}
}
printf("[%d]:%ld+%ld=%ld\\n",++count,n,m,m+n);
printf("Here we reached the aim at last!\\n");
}
long re(long int a)
{
long int t;
for(t=0;a>0;a/=10)
t=t*10+a%10;
return t;
}
int nonres(long int s)
{
if(re(s)==s) return 1;
else return 0;
}
一副扑克有52张牌,打桥牌时应将牌分给四个人。请设计一个程序完成自动发牌的工作。要求:黑桃用S(Spaces)表示;红桃用H(Hearts)表示;方块用D(Diamonds)表示;梅花用C(Clubs)表示。
*问题分析与算法设计
按照打桥牌的规定,每人应当有13张牌。在人工发牌时,先进行洗牌,然后将洗好的牌按一定的顺序发给每一个人。为了便于计算机模拟,可将人 工方式的发牌过程加以修改:先确定好发牌顺序:1、2、3、4;将52张牌顺序编号:黑桃2对应数字0,红桃2对应数字1,方块2对应数字2,梅花2对应 数字3,黑桃3对应数字4,红桃3对应数字5,...然后从52 张牌中随机的为每个人抽牌。
这里采用C语言库函数的随机函数,生成0到51之间的共52个随机数,以产生洗牌后发牌的效果。
*程序与程序注释
#include<stdlib.h>
#include<stdio.h>
int comp(const void *j,const void *i);
void p(int b[],char n[]);
void main()
{
static char n[]={\'2\',\'3\',\'4\',\'5\',\'6\',\'7\',\'8\',\'9\',\'T\',\'J\',\'Q\',\'K\',\'A\'};
int a[53],b1[13],b2[13],b3[13],b4[13];
int b11=0,b22=0,b33=0,b44=0,t=1,m,flag,i;
while(t<=52)
{ m=random(52);
for(flag=1,i=1;i<=t&&flag;i++)
if(m==a) flag=0;
if(flag)
{
a[t++]=m;
if(t%4==0) b1[b11++]=a[t-1];
else if(t%4==1) b2[b22++]=a[t-1];
else if(t%4==2) b3[b33++]=a[t-1];
else if(t%4==3) b4[b44++]=a[t-1];
}
}
qsort(b1,13,sizeof(int),comp);
qsort(b2,13,sizeof(int),comp);
qsort(b3,13,sizeof(int),comp);
qsort(b4,13,sizeof(int),comp);
p(b1,n); p(b2,n); p(b3,n); p(b4,n);
}
void p(int b[],char n[])
{
int i;
printf("\\n\\006 ");
for(i=0;i<13;i++)
if(b/13==0) printf("%c ",n[b%13]);
printf("\\n\\003 ");
for(i=0;i<13;i++)
if((b/13)==1) printf("%c ",n[b%13]);
printf("\\n\\004 ");
for(i=0;i<13;i++)
if(b/13==2) printf("%c ",n[b%13]);
printf("\\n\\005 ");
for(i=0;i<13;i++)
if(b/13==3||b/13==4) printf("%c ",n[b%13]);
printf("\\n");
}
int comp(const void *j,const void *i)
{
return(*(int*)i-*(int*)j);
}
有三个白子和三个黑子如下图布置:
○ ○ ○ . ● ● ●
游戏的目的是用最少的步数将上图中白子和黑子的位置进行交换:
● ● ● . ○ ○ ○
游戏的规则是:(1)一次只能移动一个棋子; (2)棋子可以向空格中移动,也可以跳过一个对方的棋子进入空格,但不能向后跳,也不能跳过两个子。请用计算机实现上述游戏。
*问题分析与算法设计
计算机解决胜这类问题的关键是要找出问题的规律,或者说是要制定一套计算机行动的规则。分析本题,先用人来解决问题,可总结出以下规则:
(1) 黑子向左跳过白子落入空格,转(5)
(2) 白子向右跳过黑子落入空格,转(5)
(3) 黑子向左移动一格落入空格(但不应产生棋子阻塞现象),转(5)
(4) 白子向右移动一格落入空格(但不应产生棋子阻塞现萌),转(5)
(5) 判断游戏是否结束,若没有结束,则转(1)继续。
所谓的“阻塞”现象就是:在移动棋子的过程中,两个尚未到位的同色棋子连接在一起,使棋盘中的其它棋子无法继续移动。例如按下列方法移动棋子:
0
○ ○ ○ . ● ● ●
1 ○ ○ . ○ ● ● ●
2 △ ○ ○ ● ○ . ● ●
3
○ ○ ● . ○ ● ●
4 两个●连在一起产生阻塞
○ ○ ● ● ○ . ●
或4 两个白连在一起产生阻塞
○ . ● ○ ○ ● ●
产生阻塞的现象的原因是在第2步(△状态)时,棋子○不能向右移动,只能将●向左移动。
总结产生阻塞的原因,当棋盘出现“黑、白、空、黑”或“白、空、黑、白”状态时,不能向左或向右移动中间的棋子,只移动两边的棋子。
按照上述规则,可以保证在移动棋子的过程中,不会出现棋子无法移动的现象,且可以用最少的步数完成白子和黑子的位置交换。
*程序与程序注释
#include<stdio.h>
int number;
void print(int a[]);
void change(int *n,int *m);
void main()
{
int t[7]={1,1,1,0,2,2,2};
int i,flag;
print(t);
while(t[0]+t[1]+t[2]!=6||t[4]+t[5]+t[6]!=3)
{
flag=1;
for(i=0;flag&&i<5;i++)
if(t==1&&t[i+1]==2&&t[i+2]==0)
{change(&t,&t[i+2]); print(t); flag=0;}
for(i=0;flag&&i<5;i++)
if(t==0&&t[i+1]==1&&t[i+2]==2)
{change(&t,&t[i+2]); print(t); flag=0;}
for(i=0;flag&&i<6;i++)
if(t==1&&t[i+1]==0&&(i==0||t[i-1]!=t[i+2]))
{change(&t,&t[i+1]); print(t);flag=0;}
for(i=0;flag&&i<6;i++)
if(t==0&&t[i+1]==2&&(i==5||t[i-1]!=t[i+2]))
{ change(&t,&t[i+1]); print(t);flag=0;}
}
}
void print(int a[])
{
int i;
printf("No. %2d:.............................\\n",number++);
printf(" ");
for(i=0;i<=6;i++)
printf(" | %c",a==1?\'*\':(a==2?\'@\':\' \'));
printf(" |\\n .............................\\n\\n");
}
void change(int *n,int *m)
{
int term;
term=*n; *n=*m; *m=term;
}
--------------------------------------------------------------------------------
现有21根火柴,两人轮流取,每人每次可以取走1至4根,不可多取,也不能不取,谁取最后一楰火柴谁输。请编写一个程序进行人机对弈,要求人先取,计算机后取;计算机一方为“常胜将军”。
*问题分析与算法设计
在计算机后走的情况下,要想使计算机成为“常胜将军”,必须找出取 关键。根据本题的要求枷以总结出,后走一方取子的数量与对方刚才一步取子的数量之和等于,就可以保证最后一个子是留给先取子的那个人的。
据此分析进行算法设计就是很简单的工作,编程实现也十分容易。
*程序与程序注释
#include<stdio.h>
void main()
{
int a=21,i;
printf("Game begin:\\n");
while(a>0)
{
do{
printf("How many stick do you wish to take(1~%d)?",a>4?4:a);
scanf("%d",&i);
}while(i>4||i<1||i>a);
if(a-i>0) printf(" %d stick left in the pile.\\n",a-i);
if((a-i)<=0)
{
printf(" You have taken the last stick.\\n");
printf(" * * * You lose! \\nGame Over.\\n");
break;
}
else
printf(" Compute take %d stick.\\n",5-i);
a-=5;
printf(" %d stick left in the pile.\\n",a);
}
}
这是中国民间的一个游戏。两人从1开始轮流报数,每人每次可报一个数或两个连续的数,谁先报到30,谁就为胜方。
*问题分析与算法设计
本题与上题类似,算法也类似,所不同的是,本谁先走第一步是可选的。若计算机走第一步,那么计算机一定是赢家。若人先走一步,那么计算机只好等待人犯错误,如果人先走第一步且不犯错误,那么人就会取胜;否则计算机会抓住人的一次错误使自己成为胜利者。
*程序与程序注释
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
int input(int t);
int copu(int s);
void main()
{
int tol=0;
printf("\\n* * * * * * * *catch thirty* * * * * * * \\n");
printf("Game Begin\\n");
randomize();
if(random(2)==1)
tol=input(tol);
while(tol!=30)
if((tol=copu(tol))==30)
printf("I lose! \\n");
else
if((tol=input(tol))==30)
printf("I lose! \\n");
printf(" * * * * * * * *Game Over * * * * * * * *\\n");
}
int input(int t)
{
int a;
do{
printf("Please count:");
scanf("%d",&a);
if(a>2||a<1||t+a>30)
printf("Error input,again!");
else
printf("You count:%d\\n",t+a);
}while(a>2||a<1||t+a>30);
return t+a;
}
int copu(int s)
{
int c;
printf("Computer count:");
if((s+1)%3==0)
printf(" %d\\n",++s);
else if((s+2)%3==0)
{
s+=2;
printf(" %d\\n",s);
}
else
{
c=random(2)+1;
s+=c;
printf(" %d\\n",s);
}
return s;
}
设有n座山,计算机与人为比赛的双方,轮流搬山。规定每次搬山的数止不能超 过k座,谁搬最后一座谁输。游戏开始时。计算机请人输入山的总数(n)和每次允许搬山的最大数止(k)。然后请人开始,等人输入了需要搬走的山的数目后, 计算机马上打印出它搬多少座山,并提示尚余多少座山。双方轮流搬山直到最后一座山搬完为止。计算机会显示谁是赢家,并问人是否要继续比赛。若人不想玩了, 计算机便会统计出共玩了几局,双方胜负如何。
*问题分析与算法设计
计算机参加游戏时应遵循下列原则:
1) 当:
剩余山数目-1<=可移动的最大数k 时计算机要移(剩余山数目-1)座,以便将最后一座山留给人。
2)对于任意正整数x,y,一定有:
0<=x%(y+1)<=y
在有n座山的情况下,计算机为了将最后一座山留给人,而且又要控制每次搬山的数目不超过最大数k,它应搬山的数目要满足下列关系:
(n-1)%(k+1)
如果算出结果为0,即整除无余数,则规定只搬1座山,以防止冒进后发生问题。
按照这样的规律,可编写出游戏程序如下:
#include<stdio.h>
void main()
{
int n,k,x,y,cc,pc,g;
printf("More Mountain Game\\n");
printf("Game Begin\\n");
pc=cc=0;
g=1;
for(;;)
{
printf("No.%2d game \\n",g++);
printf("---------------------------------------\\n");
printf("How many mpuntains are there?");
scanf("%d",&n);
if(!n) break;
printf("How many mountains are allowed to each time?");
do{
scanf("%d",&k);
if(k>n||k<1) printf("Repeat again!\\n");
}while(k>n||k<1);
do{
printf("How many mountains do you wish movw away?");
scanf("%d",&x);
if(x<1||x>k||x>n)
{
printf("IIIegal,again please!\\n");
continue;
}
n-=x;
printf("There are %d mountains left now.\\n",n);
if(!n)
{
printf("...............I win. You are failure...............\\n\\n");cc++;
}
else
{
y=(n-1)%(k+1);
if(!y) y=1;
n-=y;
printf("Copmputer move %d mountains away.\\n",y);
if(n) printf(" There are %d mountains left now.\\n",n);
else
{
printf("...............I am failure. You win..................\\n\\n");
pc++;
}
}
}while(n);
}
printf("Games in total have been played %d.\\n",cc+pc);
printf("You score is win %d,lose %d.\\n",pc,cc);
printf("My score is win %d,lose %d.\\n",cc,pc);
}
--------------------------------------------------------------------------------
由计算机“想”一个四位数,请人猜这个四位数是多少。人输入四位数字后,计算机首先判断这四位数字中有几位是猜对了,并且在对的数字中又有几位位置也是对的,将结果显示出来,给人以提示,请人再猜,直到人猜出计算机所想的四位数是多少为止。
例如:计算机“想”了一个“1234”请人猜,可能的提示如下:
人猜的整数 计算机判断有几个数字正确 有几个位置正确
1122 2 1
3344 2 1
3312 3 0
4123 4 0
1243 4 2
1234 4 4
游戏结束
请编程实现该游戏。游戏结束时,显示人猜一个数用了几次。
*问题分析与算法设计
问题本身清楚明了。判断相同位置上的数字是否相同不需要特殊的算法。只要截取相同位置上的数字进行比较即可。但在判断几位数字正确时,则应当注意:计算机所想的是“1123”,而人所猜的是“1576”,则正确的数字只有1位。
程序中截取计算机所想的数的每位数字与人所猜的数字按位比较。若有两位数字相同,则要记信所猜中数字的位置,使该位数字只能与一位对应的数字 “相同”。当截取下一位数字进行比较时,就不应再与上述位置上的数字进行比较,以避免所猜的数中的一位与对应数中多位数字“相同”的错误情况。
*程序与程序注释
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void main()
{
int stime,a,z,t,i,c,m,g,s,j,k,l[4];
long ltime;
ltime=time(NULL);
stime=(unsigned int)ltime/2;
srand(stime);
z=random(9999);
printf("I have a number with 4 digits in mind,please guess.\\n");
for(c=1;;c++)
{
printf("Enter a number with 4 digits:");
scanf("%d",&g);
a=z;j=0;k=0;l[0]=l[1]=l[2]=l[3]=0;
for(i=1;i<5;i++)
{
s=g;m=1;
for(t=1;t<5;t++)
{
if(a%10==s%10)
{
if(m&&t!=l[0]&&t!=l[1]&&t!=l[2]&&t!=l[3])
{
j++;m=0;l[j-1]=t;
}
if(i==t) k++;
}
s/=10;
}
a/=10;
}
printf("You hane correctly guessed %d digits,\\n",j);
printf("and correctly guessed %d digits in exact position.\\n",k);
if(k==4) break;
}
printf("Now you have correctly guessed the whole number after %d times.\\n",c);
}
将以上游戏双方倒一下,请人想一个四位的整数,计算机来猜,人给计算机提示信息,最终看计算机用几次猜出一个人“想”的数。请编程实现。
*问题分析与算法设计
解决这类问题时,计算机的思考过程不可能象人一样具完备的推理能力,关键在于要将推理和判断的过程变成一种机械的过程,找出相应的规则,否则计算机难以完成推理工作。
基于对问题的分析和理解,将问题进行简化,求解分为两个步聚来完成:首先确定四位数字的组成,然后再确定四位数字的排列顺序。可以列出如下规则:
1)分别显示四个1,四个2,......,四个0,确定四位数字的组成。
2)依次产生四位数字的全部排列(依次两两交换全部数字的位置)。
3)根据人输入的正确数字及正确位置的数目,进行分别处理:
(注意此时不出现输入的情况,因为在四个数字已经确定的情况下,若有3个位置正确,则第四个数字的位置必然也是正确的)
若输入4:游戏结束。
判断本次输入与上次输入的差值
若差为2:说明前一次输入的一定为0,本次输入的为2,本次交换的两个数字的位置是正确的,只要交换另外两个没有交换过的数字即可结束游戏。
若差为-2:说明前一次输入的一定为2,本次的一定为0。说明刚交换过的两个数字的位置是错误的,只要将交换的两个数字位置还原,并交换另外两个没有交换过的数字即可结束游戏。
否则:若本次输入的正确位置数<=上次的正确位置数
则恢复上次四位数字的排列,控制转3)
否则:将本次输入的正确位置数作为“上次输入的正确位置数”,控制转3)。
*程序与程序注释
#include<stdio.h>
#include<stdlib.h>
void bhdy(int s,int b);
void prt();
int a[4],flag,count;
void main()
{
int b1,b2,i,j,k=0,p,c;
printf("Game guess your number in mind is # # # #.\\n");
for(i=1;i<10&&k<4;i++)
{
printf("No.%d:your number may be:%d%d%d%d\\n",++count,i,i,i,i);
printf("How many digits have bad correctly guessed:");
scanf("%d",&p);
for(j=0;j<p;j++)
a[k+j]=i;
k+=p;
}
if(k<4)
for(j=k;j<4;j++)
a[j]=0;
i=0;
printf("No.%d:your number may be:%d%d%d%d\\n",++count,a[0],a[1],a[2],a[3]);
printf("How many are in exact positions:");
scanf("%d",&b1);
if(b1==4){prt();exit(0);}
for(flag=1,j=0;j<3&&flag;j++)
for(k=j+1;k<4&&flag;k++)
if(a[j]!=a[k])
{
c=a[j];a[j]=a[k];a[k]=c;
printf("No.%d:Your number may be: %d%d%d%d\\n",++count,a[0],a[1],a[2],a[3]);
printf("How many are in exact positins:");
scanf("%d",&b2);
if(b2==4){prt();flag=0;}
else if(b2-b1==2)bhdy(j,k);
else if(b2-b1==-2)
{
c=a[j];a[j]=a[k];a[k]=c;
bhdy(j,k);
}
else if(b2<=b1)
{
c=a[j];a[j]=a[k];a[k]=c;
}
else b1=b2;
}
if(flag) printf("You input error!\\n");
}
void prt()
{
printf("Now your number must be %d%d%d%d.\\n",a[0],a[1],a[2],a[3]);
printf("Game Over\\n");
}
void bhdy(int s,int b)
{
int i,c=0,d[2];
for(i=0;i<4;i++)
if(i!=s&&i!=b) d[c++]=i;
i=a[d[1>;a[d[1>=a[d[0>; a[d[0>=i;
prt();
flag=0;
}
约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
*问题分析与算法设计
这是一个著名的问题,几乎所有的教材上都有这个问题。由于条件是一次只能移动一个盘,且不允许大盘放在小盘上面,所以64个盘的移动次数是:
18,446,744,073,709,551,615
这是一个天文数字,若每一微秒可能计算(并不输出)一次移动,那么也需要几乎一百万年。我们仅能找出问题的解决方法并解决较小N值时的汉诺塔,但很难用计算机解决64层的汉诺塔。
分析问题,找出移动盘子的正确算法。
首先考虑a杆下面的盘子而非杆上最上面的盘子,于是任务变成了:
*将上面的63个盘子移到b杆上;
*将a杆上剩下的盘子移到c杆上;
*将b杆上的全部盘子移到c杆上。
将这个过程继续下去,就是要先完成移动63个盘子、62个盘子、61个盘子....的工作。
为了更清楚地描述算法,可以定义一个函数movedisc(n,a,b,c)。该函数的功能是:将N个盘子从A杆上借助C杆移动到B杆上。这样移动N个盘子的工作就可以按照以下过程进行:
1) movedisc(n-1,a,c,b);
2) 将一个盘子从a移动到b上;
3) movedisc(n-1,c,b,a);
重复以上过程,直到将全部的盘子移动到位时为止。
*程序与程序注释
#include<stdio.h>
void movedisc(unsigned n,char fromneedle,char toneedle,char usingneedle);
int i=0;
void main()
{
unsigned n;
printf("please enter the number of disc:");
scanf("%d",&n);
printf("\\tneedle:\\ta\\t b\\t c\\n");
movedisc(n,\'a\',\'c\',\'b\');
printf("\\t Total: %d\\n",i);
}
void movedisc(unsigned n,char fromneedle,char toneedle,char usingneedle)
{
if(n>0)
{
movedisc(n-1,fromneedle,usingneedle,toneedle);
++i;
tch(fromneedle)
{
case \'a\':tch(toneedle)
{
case \'b\': printf("\\t[%d]:\\t%2d.........>%2d\\n",i,n,n);
break;
case \'c\': printf("\\t[%d]:\\t%2d...............>%2d\\n",i,n,n);
break;
}
break;
case \'b\':tch(toneedle)
{
case \'a\': printf("\\t[%d]:\\t%2d<...............>%2d\\n",i,n,n);
break;
case \'c\': printf("\\t[%d]:\\t %2d........>%2d\\n",i,n,n);
break;
}
break;
case \'c\':tch(toneedle)
{
case \'a\': printf("\\t[%d]:\\t%2d<............%2d\\n",i,n,n);
break;
case \'b\': printf("\\t[%d]:\\t%2d<........%2d\\n",i,n,n);
break;
}
break;
}
movedisc(n-1,usingneedle,toneedle,fromneedle);
}
}
从前有一对长寿兎子,它们每一个月生一对兎子,新生的小兎子两个月就长大了,在第二个月的月底开始生它们的下一代小兎子,这样一代一代生下去,求解兎子增长数量的数列。
*问题分析与算法设计
问题可以抽象成下列数学公式:
Un=Un-1+Un-2
其中:
n是项数(n>=3)。它就是著名的菲波那奇数列,该数列的前几为:1,1,2,3,5,8,13,21...
菲波那奇数列在程序中可以用多种方法进行处理。按照其通项递推公式利用最基本的循环控制就可以实现题目的要求。
*程序与程序注释
#include<stdio.h>
void main()
{
int n,i,un1,un2,un;
for(n=2;n<3;)
{
printf("Please enter required number of generation:");
scanf("%d",&n);
if(n<3) printf("\\n Enter error!\\n");
}
un=un2=1;
printf("The repid increase of rabbits in first %d generation is as felow:\\n",n);
printf("l\\tl\\t");
for(i=3;i<=n;i++)
{
un1=un2;
un2=un;
un=un1+un2;
printf(i%10?"%d\\t":"%d\\n",un);
}
printf("\\n");
}
*运行结果
Please enter required number of generation: 20
The repid increase of rabbits in first 20 generation is as felow:
1 1 2 3 5 8 13 21 34 55
89 144 233 377 610 987 1597 2584 4181 6765
将大于0小于1000的阿拉伯数字转换为罗马数字。
*问题分析与算法设计
题目中给出了阿拉伯数字与罗马数字的对应关系,题中的数字转换实际上就是查表翻译。即将整数的百、十、个位依次从整数中分解出来,查找表中相应的行后输出对应的字符。
*程序与程序设计
#include<stdio.h>
void main()
{
static char *a[][10]={"","I","II","III","IV","V","VI","VII","VIII","IX"
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XCC",
"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"
};
int n,t,i,m;
printf("Please enter number:");
scanf("%d",&n);
printf("%d=",n);
for(m=0,i=1000;m<3;m++,i/=10)
{
t=(n%i)/(i/10);
printf("%s",a[2-m][t]);
}
printf("\\n");
}
*运行结果
1. Please enter number:863
863=DCCCLXIII
2. Please enter number: 256
256=CCLVI
3. Please enter number:355
355=CCCLV
4. Please enter number:522
522=DXXII
5. Please enter number:15
15=XV
*思考题
输入正整数N,产生对应的英文数字符串并输出,例如:
1 ONE 2 TWO 3 THREE
10 TEN 11 ELEVEN
135 ONE HUNDRED THIRTY FIVE
--------------------------------------------------------------------------------
在选美大奖赛的半决胜赛现场,有一批选手参加比赛,比赛的规则是最后得分越高,名次越低。当半决决赛结束时,要在现场按照选手的出场顺序宣布最后得分和最 后名次,获得相同分数的选手具有相同的名次,名次连续编号,不用考虑同名次的选手人数。例如:
选手序号: 1,2,3,4,5,6,7
选手得分: 5,3,4,7,3,5,6
则输出名次为: 3,1,2,5,1,3,4
请编程帮助大奖赛组委会完成半决赛的评分和排名工作。
*问题分析与算法设计
问题用程序设计语言加以表达的话,即为:将数组A中的整数从小到大进行连续编号,要求不改变数组中元素的顺序,且相同的整数要具有相同的编号。
普通的排序方法均要改变数组元素原来的顺序,显然不能满足要求。为此,引入一个专门存放名次的数组,再采用通常的算法:在尚未排出名次的元素中找出最小 值,并对具有相同值的元素进行处理,重复这一过程,直到全部元素排好为止。
*程序与程序注释
#include<stdio.h>
#define NUM 7
int a[NUM+1]={0,5,3,4,7,3,5,6};
int m[NUM+1],l[NUM+1];
void main()
{
int i,smallest,num,k,j;
num=1;
for(i=1;i<=NUM;i++)
if(m==0)
{
smallest=a;
k=1;
l[k]=i;
for(j=i+1;j<=NUM;j++)
if(m[j]==0)
if(a[j]<smallest)
{
smallest=a[j];
k=0;
l[++k]=j;
}
else if(a[j]==smallest)
l[++k]=j;
for(j=1;j<=k;j++)
m[l[j>=num;
num++;
i=0;
}
printf("Player-No score Rank\\n");
for(j=1;j<=NUM;j++)
printf(" %3d %4d %4d\\n",j,a[j],m[j]);
}
*运行结果
Player-No Score Rank
1 5 3
2 3 1
3 4 2
5 7 5
5 3 1
3 5 3
7 6 4
*思考题
若将原题中的“名次连续编号,不用考虑同名次的选手人数”,改为”根据同名次的选手人数对选手的名次进行编号“,那么应该怎样修改程序。
输入m和n(20>=m>=n>0)求出满足以下方程的正整数数列 i1,i2,...,in,使得:i1+i1+...+in=m,且i1>=i2...>=in。例如:
当n=4, m=8时,将得到如下5 个数列:
5 1 1 1 4 2 1 1 3 3 1 1 3 2 2 1 2 2 2 2
*问题分析与算法设计
可将原题抽象为:将M分解为N个整数,且N个整数的和为M,i1>=i2>=...>=in。分解整数的方法很低多,由于 题目中 有"i1>=i2>=.....>=in,提示我们可先确定最右边in元素的值为1,然后按照条件使前一个元素的值一定大于等于当前元 素的值,不断地向前推就可以解决问题。下面的程序允许用户选定M和N,输出满足条件的所有数列。
*程序与程序注释
#include<stdio.h>
#define NUM 10
int i[NUM];
void main()
{
int sum,n,total,k,flag,count=0;
printf("Please enter requried terms(<=10):");
scanf("%d",&n);
printf(" their sum:");
scanf("%d",&total);
sum=0;
k=n;
i[n]=1;
printf("There are following possible series:\\n");
while(1)
{
if(sum+i[k]<total)
if(k<=1)
{i[1]=total-sum;flag=1;}
else{
sum+=i[k--];
i[k]=i[k+1];
continue;
}
else if(sum+i[k]>total||k!=1)
{ sum-=i[++k]; flag=0;}
else flag=1;
if(flag)
{
printf("[%d]:",++count);
for(flag=1;flag<=n;++flag)
printf("%d",i[flag]);
printf("\\n");
}
if(++k>n)
break;
sum-=i[k];
i[k]++;
}
}
*运行结果
Please enter requried terms(<=10):4
their sum:8
There are following possible series:
[1]: 5111
[2]: 4211
[3]: 3311
[4]: 3221
[5]: 2222
在一个8×8国际象棋盘上,有8个皇后,每个皇后占一格;要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行、同一列或同一对角线上。问 共有多少种不同的方法。
*问题分析与算法设计
这是一个古老的具有代表性的问题,用计算机求解时的算法也很多,这里仅介绍一种。
采用一维数组来进行处理。数组的下标i表示棋盘上的第i列,a的值表示皇后在第i列所放的位置。如:a[1]=5,表示在棋盘的第一例的第五行放一个皇 后。
程序中首先假定a[1]=1,表示第一个皇后放在棋盘的第一列的第一行的位置上,然后试探第二列中皇后可能的位置,找到合适的位置后,再处理后续的各列, 这样通过各列的反复试探,可以最终找出皇后的全部摆放方法。
程序采用回溯法,算法的细节参看程序。
*程序与程序注释
#include<stdio.h>
#define NUM 8
int a[NUM+1];
void main()
{
int i,k,flag,not_finish=1,count=0;
i=1;
a[1]=1;
printf("The possible configuration of 8 queens are:\\n");
while(not_finish)
{
while(not_finish&&i<=NUM)
{
for(flag=1,k=1;flag&&k<i;k++)
if(a[k]==a)flag=0;
for(k=1;flag&&k<i;k++)
if((a==a[k]-(k-i))||(a==a[k]+(k-i))) flag=0;
if(!flag)
{
if(a==a[i-1])
{
i--;
if(i>1&&a==NUM)
a=1;
else if(i==1&&a==NUM)
not_finish=0;
else a++;
}
else if(a==NUM) a=1;
else a++;
}
else if(++i<=NUM)
if(a[i-1]==NUM) a=1;
else a=a[i-1]+1;
}
if(not_finish)
{
++count;
printf((count-1)%3?" [%2d]: ":" \\n[%2d]: ",count);
for(k=1;k<=NUM;k++)
printf(" %d",a[k]);
if(a[NUM-1]<NUM) a[NUM-1]++;
else a[NUM-1]=1;
i=NUM-1;
}
}
}
请设计一个算法来完成两个超长正整数的加法。
*问题分析与算法设计
首先要设计一种数据结构来表示一个超长的正整数,然后才能够设计算法。
首先我们采用一个带有表头结点的环形链来表示一个非负的超大整数,如果从低位开始为每 个数字编号,则第一位到第四位、第五位到第八位...的每四位组成的数字,依次放在链表的第一个、第二个、...结点中,不足4位的最高位存放在链表的最 后一个结点中,表头结点的值规定为-1。例如:
大整数“587890987654321”可用如下的带表头结点head的链表表示:
按照此数据结构,可以从两个表头结点开始,顺序依次对应相加,求出所需要的进位后代入下面的运算。具体的实现算法请见程序中的注释。
*程序与程序注释
#include<stdio.h>
#include<stdlib.h>
#define HUNTHOU 10000
typedef struct node{ int data;
struct node *next;
}NODE;
NODE *insert_after(NODE *u,int num);
NODE *addint(NODE *p,NODE *q);
void printint(NODE *s);
NODE *inputint(void);
void main()
{
NODE *s1,*s2,*s;
NODE *inputint(), *addint(), *insert_after();
printf("Enter S1= ");
s1=inputint();
printf("Enter S2= ");
s2=inputint();
printf(" S1="); printint(s1); putchar(\'\\n\');
printf(" S2="); printint(s2); putchar(\'\\n\');
s=addint(s1,s2);
printf("S1+S2="); printint(s); putchar(\'\\n\');
}
NODE *insert_after(NODE *u,int num)
{
NODE *v;
v=(NODE *)malloc(sizeof(NODE));
v->data=num;
u->next=v;
return v;
}
NODE *addint(NODE *p,NODE *q)
{
NODE *pp,*qq,*r,*s,*t;
int total,number,carry;
pp=p->next; qq=q->next;
s=(NODE *)malloc(sizeof(NODE));
s->data=-1;
t=s; carry=0;
while(pp->data!=-1&&qq->data!=-1)
{
total=pp->data+qq->data+carry;
number=total%HUNTHOU;
carry=total/HUNTHOU;
t=insert_after(t,number);
pp=pp->next;
qq=qq->next;
}
r=(pp->data!=-1)?pp:qq;
while(r->data!=-1)
{
total=r->data+carry;
number=total%HUNTHOU;
carry=total/HUNTHOU;
t=insert_after(t,number);
r=r->next;
}
if(carry) t=insert_after(t,1);
t->next=s;
return s;
}
NODE *inputint(void)
{
NODE *s,*ps,*qs;
struct number {int num;
struct number *np;
}*p,*q;
int i,j,k;
long sum;
char c;
p=NULL;
while((c=getchar())!=\'\\n\')
if(c>=\'0\'&&c<=\'9\')
{
q=(struct number *)malloc(sizeof(struct number));
q->num=c-\'0\';
q->np=p;
p=q;
}
s=(NODE *)malloc(sizeof(NODE));
s->data=-1;
ps=s;
while(p!=NULL)
{
sum=0;i=0;k=1;
while(i<4&&p!=NULL)
{
sum=sum+k*(p->num);
i++; p=p->np; k=k*10;
}
qs=(NODE *)malloc(sizeof(NODE));
qs->data=sum;
ps->next=qs;
ps=qs;
}
ps->next=s;
return s;
}
void printint(NODE *s)
{
if(s->next->data!=-1)
{
printint(s->next);
if(s->next->next->data==-1)
printf("%d",s->next->data);
else{
int i,k=HUNTHOU;
for(i=1;i<=4;i++,k/=10)
putchar(\'0\'+s->next->data%(k)/(k/10));
}
}
}
0--------0----------0
| \\ | / |
0--------0----------0
| / | \\ |
0--------0----------0
在图中的九个点上,空出中间的点,其余的点上任意填入数字1到8;1的位置固定不动,然后移动其余的数字,使1到8顺时针从小到大排列.移动的规律是:只 能将数字沿线移向空白的点.
请编程显示数字移动过程。
*题目分析与算法设计
分析题目中的条件,要求利用中间的空白格将数字顺时针方向排列,且排列过程中只能借空白的点来移动数字.问题的实质就是将矩阵外面的8个格看 成一个环,8 个数字在环内进行排序,同于受题目要求的限制"只能将数字沿线移向空白的点",所以要利用中间的空格进行排序,这样要求的排序算法与众不同.
观察中间的点,它是唯一一个与其它8个点有连线的点,即它是中心点.中心点的活动的空间最大,它可以向8个方向移动,充分利用中心点这个特性是算法设计成 功与否的关键.
在找到1所在的位置后,其余各个数字的正确位置就是固定的.我们可以按照下列算法从数字2开始,一个一个地来调整各个数字的位置.
*确定数字i应处的位置;
*从数字i应处的位置开始,向后查找数字i现在的位置;
*若数字i现在位置不正确,则将数字i从现在的位置(沿连线)移向中间的空格,而将原有位置空出;依次将现有空格前的所有元素向后移动;直到将i应处的位 置空出,把它移入再次空出中间的格.
从数字2开始使用以上过程,就可以完成全部数字的移动排序.
编程时要将矩阵的外边八个格看成一个环,且环的首元素是不定的,如果算法设计得不好,程序中就要花很多精力来处理环中元素的前后顺序问题.将 题目中的 3X3矩阵用一个一维数组表示,中间的元素(第四号)刚好为空格,设计另一个指针数组,专门记录指针外八个格构成环时的连接关系.指针数组的每个元素依次 记录环中数字在原来数组中对应的元素下标.这样通过指针数组将原来矩阵中复杂的环型关系表示成了简单的线性关系,从而大大地简化了程序设计.
*程序与程序注释
#include<stdio.h>
int a[]={0,1,2,5,8,7,6,3};
int b[9];
int c[9];
int count=0;
void main()
{
int i,j,k,t;
void print();
printf("Please enter original order of digits 1~8:");
for(i=0;i<8;i++)
scanf("%d",&b[a[i>);
printf("The sorting process is as felow:\\n");
print();
for(t=-1,j=0;j<8&&t==-1;j++)
if(b[a[j>==1) t=j;
for(j=0;j<8;j++)
c[j]=a[(j+t)%8];
for(i=2;i<9;i++)
for(j=i-1;j<8;j++)
if(b[c[j>==i&&j!=i-1)
{
b[4]=i;
b[c[j>=0;print();
for(k=j;k!=i-1;k--)
{
b[c[k>=b[c[k-1>;
b[c[k-1>=0;
print();
}
b[c[k>=i;
b[4]=0;
print();
break;
}
else if(b[c[j>==i) break;
}
void print(void)
{
int c;
for(c=0;c<9;c++)
if(c%3==2) printf("%2d ",b[c]);
else printf("%2d",b[c]);
printf("----%2d----\\n",count++);
}
--------------------------------------------------------------------------------