趣味程序设计编程百例精解
更新时间:2024-06-15 12:19:01 阅读量: 综合文库 文档下载
- 程序设计和编程的区别推荐度:
- 相关推荐
C/C++语言经典、实用、趣味程序设计编程百例精解(1) 1.绘制余弦曲线
在屏幕上用―*‖显示0~360度的余弦函数cos(x)曲线 *问题分析与算法设计
如果在程序中使用数组,这个问题十分简单。但若规定不能使用数组,问题就变得不容易了。
关键在于余弦曲线在0~360度的区间内,一行中要显示两个点,而对一般的显示器来说,只能按行输出,即:输出第一行信息后,只能向下一行输出,不能再返回到上一行。为了获得本文要求的图形就必须在一行中一次输出两个―*‖。
为了同时得到余弦函数cos(x)图形在一行上的两个点,考虑利用*问题分析与算法设计
本题可以在上题的基础上进行修改。图形迭加的关键是要在分别计算出同一行中两个图形的列方向点坐标后,正确判断相互的位置关系。为此,可以先判断图形的交点,再分别控制打印两个不同的图形。 *程序注释与说明 #include
double y; int x,m,n,yy;
for(yy=0;yy<=20;yy++) /*对于第一个y坐标进行计算并在一cos(x)的左右对称性。将屏幕的行方向定义为x,列方向定义为y,则0~180度的图形与180~360度的图形是左右对称的,若定义图形的总宽度为62列,计算出x行0~180度时y点的坐标m,那么在同一行与之对称的180~360度的y点的坐标就 应为62-m。程序中利用反余弦函数acos计算坐标(x,y)的对应关系。
使用这种方法编出的程序短小精炼,体现了一定的技巧。 *程序说明与注释 #include
double y; int x,m;
for(y=1;y>=-1;y-=0.1) /*y为列方向,值从1到-1,步长为0.1*/ {
m=acos(y)*10; /*计算出y对应的弧度m,乘以10为图形放大倍数*/
for(x=1;x printf(\控制打印同一行中对称的右侧*号*/ } return 0; } *思考题 如何实现用―*‖显示0~360度的sin(x)曲线。 在屏幕上显示0~360度的cos(x)曲线与直线 f(x)=45*(y-1)+31的迭加图形。其中cos(x)图形用―*‖表示,f(x)用―+‖表示,在两个图形相交的点上则用f(x)图形的符号。 2.绘制余弦曲线和直线 行中打印图形*/ { y=0.1*yy; /*y:屏幕行方向坐标*/ m=acos(1-y)*10; /*m: cos(x)曲线上y点对应的屏幕列坐标*/ n=45*(y-1)+31; /*n: 直线上y点对应的列坐标*/ for(x=0;x<=62;x++) /*x: 屏幕列方向坐标*/ if(x==m&&x==n) printf(\直线与cos(x)相交时打印―+‖*/ else if(x==n) printf(\打印不相交时的直线图形*/ else if(x==m||x==62-m) printf(\打印不相交时的cos(x)图形*/ else printf(\其它情况打印空格*/ printf(\} return 0; } *思考题 如何实现sin(x)曲线与cos(x)曲线图形的同时显示。 3.绘制圆 在屏幕上用―*‖画一个空心的圆 *问题分析与算法设计 打印圆可利用图形的左右对称性。根据圆的方程: R*R=X*X+Y*Y 可以算出圆上每一点行和列的对应关系。 *程序说明与注释 #include double y; int x,m; for(y=10;y>=-10;y–) - 1 - { m=2.5*sqrt(100-y*y); /*计算行y对应的列坐标m,2.5是屏幕纵横比调节系数因为屏幕的 行距大于列距,不进行调节显示出来的将是椭圆*/ for(x=1;x<30-m;x++) printf(\图形左侧空白控制*/ printf(\圆的左侧*/ for(;x<30+m;x++) printf(\图形的空心部分控制*/ printf(\圆的右侧*/ } return 0; } *思考题 实现函数y=x2的图形与圆的图形叠加显示 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 *思考题 4.歌星大奖赛 在歌星大奖赛中,有10个评委为参赛的选手打分,分数为1~100分。选手最后得分为:去掉一个最高分和一个最低分后其余8个分数的平均值。请编写一个程序实现。 *问题分析与算法设计 这个问题的算法十分简单,但是要注意在程序中判断最大、最小值的变量是如何赋值的。 *程序说明与注释 #include int integer,i,max,min,sum; max=-32768; /*先假设当前的最大值max为C语言整型数的最小值*/ min=32767; /*先假设当前的最小值min为C语言整型数的最大值*/ sum=0; /*将求累加和变量的初值置为0*/ for(i=1;i<=10;i++) { printf(\ scanf(\输入评委的评分*/ sum+=integer; /*计算总分*/ if(integer>max)max=integer; /*通过比较筛选出其中的最高分*/ if(integer printf(\score:%d\\n\ printf(\输出结果*/ } *运行结果 Input number1=90 题目条件不变,但考虑同时对评委评分进行裁判,即在10个评委中找出最公平(即评分最接返平均分)和最不公平(即与平均分的差距最大)的评委,程序应该怎样实现? 5.求最大数 问555555的约数中最大的三位数是多少? *问题分析与算法设计 根据约数的定义,对于一个整数N,除去1和它自身外,凡能整除N的数即为N的约数。因此,最简单的方法是用2到N-1之间的所有数去除N,即可求出N的全部约数。本题只要求取约数中最大的三位数,则其取值范围可限制在100到999之间。 *程序说明与注释 #include printf(\scanf(\for(j=999;j>=100;j–) if(i%j==0) { printf(\ break; } } *运行结果 输入:555555 输出:The max factor with 3 digits in 555555 is:777 6.高次方数的尾数 求13的13次方的最后三位数 *问题分析与算法设计 解本题最直接的方法是:将13累乘13次方截取最后三位即可。- 2 - 但是由于计算机所能表示的整数范围有限,用这种―正确‖的算法不可能得到正确的结果。事实上,题目仅要求最后三位的值,完全没有必要求13的13次方的完整结果。 研究乘法的规律发现:乘积的最后三位的值只与乘数和被乘数的后三位有关,与乘数和被乘数的高位无关。利用这一规律,可以大大简化程序。 *程序说明与注释 #include int i,x,y,last=1; /*变量last保存求X的Y次方过程中的部分乘积的后三位*/ printf(\if(!(a%)) ++count; //若为25的倍数,计数器再加1 } printf(\is: %d.\\n\打印结果 return 0; } *运行结果 The number of 0 in the end of 100! is: 24. *问题进一步讨论 本题的求解程序是正确的,但是存在明显的缺点。程序中判断整数N包含多少个因子5的方法是与程序中的100有关的,若题目中的100改为1000,则就要修改程序中求因子5的数目的算scanf(\ for(i=1;i<=y;i++) /*X自乘Y次*/ last=last*x00; /*将last乘X后对1000取模,即求积的后三位*/ printf(\is:%d\\n\打印结果*/ } *运行结果 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 7.阶乘尾数零的个数 100!的尾数有多少个零? *问题分析与算法设计 可以设想:先求出100!的值,然后数一下末尾有多少个零。事实上,与上题一样,由于计算机所能表示的整数范围有限,这是不可能的。 为了解决这个问题,必须首先从数学上分析在100!结果值的末尾产生零的条件。不难看出:一个整数若含有一个因子5,则必然会在求100!时产生一个零。因此问题转化为求1到100这100个整数中包含了多少个因子5。若整数N能被25整除,则N包含2个因子5;若整数N能被5整除,则N包含1个因子5。 *程序说明与注释 #include int a,count =0; for(a=5;a<=100;a+=5) //循环从5开始,以5的倍数为步长,考察整数 { ++count; //若为5的倍数,计数器加1 法了。 *思考题 修改程序中求因子5的数目的算法,使程序可以求出任意N!的末尾有多少个零。 8.借书方案知多少 小明有五本新书,要借给A,B,C三位小朋友,若每人每次只能借一本,则可以有多少种不同的借法? *问题分析与算法设计 本问题实际上是一个排列问题,即求从5个中取3个进行排列的方法的总数。首先对五本书从1至5进行编号,然后使用穷举的方法。假设三个人分别借这五本书中的一本,当三个人所借的书的编号都不相同时,就是满足题意的一种借阅方法。 *程序说明与注释 int main() { int a,b,c,count=0; printf(\books to 3 readers:\\n\ for(a=1;a<=5;a++) /*穷举第一个人借5本书中的1本的全部情况*/ for(b=1;b<=5;b++) /*穷举第二个人借5本书中的一本的全部情况*/ for(c=1;a!=b&&c<=5;c++) /*当前两个人借不同的书时,穷举第三个人借5本书 中的1本的全部情况*/ if(c!=a&&c!=b) /*判断第三人与前两个人借的书是否不同*/ printf(count%8?\\/*打印可能的借阅方法*/ } *运行结果 There are diffrent methods for XM to distribute books to 3 readers: - 3 - 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 9.杨辉三角形 { for(j-0;j<24-2*i;j++) printf(\控制输出第i行前面的空格*/ for(j=1;j void int c(int x,int y) /*求杨辉三角形中第x行第y列的值*/ { int z; if((y==1)||(y==x+1)) return 1; /*若为x行的第1或第x+1列,则输出1*/ 在屏幕上显示杨辉三角形 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 int i,j,n=13; printf(\while(n>12) scanf(\控制输入正确的值以保证屏幕显示的图形正确*/ for(i=0;i<=n;i++) /*控制输出N行*/ z=c(x-1,y-1)+c(x-1,y); /*否则,其值为前一行中第y-1列与第y列值之和*/ return z; } *思考题 自行设计一种实现杨辉三角形的方法 10.数制转换 将任一整数转换为二进制形式 *问题分析与算法设计 将十进制整数转换为二进制的方法很多,这里介绍的实现方法利用了C语言能够对位进行操作的特点。对于C语言来说,一个整数在计算机内就是以二进制的形式存储的,所以没有必要再将一个整数经过一系列的运算转换为二进制形式,只要将整数在内存中的二进制表示输出即可。 *程序说明与注释 #include int x;printf(\scanf(\ printf(\printf(\ printb(x,sizeof(int)*8); /*x:整数 sizeof(int):int型在内存中所占的字节数 sizeof(int)*8:int型对应的位数*/ putchar('\\n'); } void printb(int x,int n) { if(n>0) { putchar('0'+((unsigned)(x&(1<<(n-1)))>>(n-1))); /*输 - 4 - 出第n位*/ printb(x,n-1); /*归调用,输出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 如果 ((年能被4除尽 且 不能被100除尽)或 能被400除尽) 则 该年是闰年; 否则 不是闰年。 C语言中判断能否整除可以使用求余运算(即求模) *程序说明与注释 #include 输出:number of decimal form:-32768 it's binary form:1000000000000000 输入:128 输出:number of decimal form:128 it's binary form:0000000010000000 *问题的进一步讨论 充分利用C语言可以对位进行操作的特点,可以编写许多其它高级语言不便于编写甚至根本无法编写的程序。位操作是C语言的一大特点,在深入学习C语言的过程中应力求很好掌握。 程序中使用的位运算方法不是最佳的,也可以不用递归操作,大家可以自行对程序进行优化。 *思考题 将任意正整数转换为四进制或八进制数 C/C++语言经典、实用、趣味程序设计编程百例精解(2) 11.打鱼还是晒网 中国有句俗语叫―三天打鱼两天晒网‖。某人从1990年1月1日起开始―三天打鱼两天晒网‖,问这个人在以后的某一天中是―打鱼‖还是―晒网‖。 *问题分析与算法设计 根据题意可以将解题过程分为三步: 1)计算从1990年1月1日开始至指定日期共有多少天; 2)由于―打鱼‖和―晒网‖的周期为5天,所以将计算出的天数用5去除; 3)根据余数判断他是在―打鱼‖还是在―晒网‖; 若 余数为1,2,3,则他是在―打鱼‖ 否则 是在―晒网‖ 在这三步中,关键是第一步。求从1990年1月1日至指定日期有多少天,要判断经历年份中是否有闰年,二月为29天,平年为28天。闰年的方法可以用伪语句描述如下: int main() { struct date today,term; int yearday,year,day; printf(\ scanf(\ay); /*输入日期*/ term.month=12; /*设置变量的初始值:月*/ term.day=31; /*设置变量的初始值:日*/ for(yearday=0,year=1990;year term.year=year; yearday+=days(term); /*计算从1990年至指定年的前一年共有多少天*/ } yearday+=days(today); /*加上指定年中到指定日期的天数*/ day=yearday%5; /*求余数*/ if(day>0&&day<4) printf(\day.\\n\打印结果*/ else printf(\} 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.year0!=0||day.year@0==0; /*判定year为闰年还是平年,lp=0为平年,非0为闰年*/ for(i=1;i day.day+=day_tab[lp][i]; - 5 - 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. *思考题 请打印出任意年份的日历 12.抓交通肇事犯 *问题分析与算法设计 分析存钱和取钱的过程,可以采用倒推的方法。若第五年年底连第五年初存款=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 一辆卡车违反交通规则,撞人后逃跑。现场有三人目击事件,但都没有记住车号,只记下车号的一些特征。甲说:牌照的前两位数字是相同的;乙说:牌照的后两位数字是相同的,但与前两位不同; 丙是数学家,他说:四位的车号刚好是一个整数的平方。请根据以上线索求出车号。 *问题分析与算法设计 按照题目的要求造出一个前两位数相同、后两位数相同且相互间又不同的整数,然后判断该整数是否是另一个整数的平方。 *程序说明与注释 #include int i,j,k,c; for(i=1;i<=9;i++) /*i:车号前二位的取值*/ for(j=0;j<=9;j++) /*j:车号后二位的取值*/ if(i!=j) /*判断二位数字是否相异*/ { k=i*1000+i*100+j*10+j; /*计算出可能的整数*/ for(c=31;c*c *运行结果 Lorry _No.is 7744 13.该存多少钱 假设银行一年整存零取的月息为0.63%。现在某人手中有一笔钱,他打算在今后的五年中的年底取出1000元,到第五年时刚好取完,请算出他存钱时应存入多少。 { int i; float total=0; for(i=0;i<5;i++) /*i 为年数,取值为0~4年*/ total=(total+1000)/(1+0.0063*12); /*累计算出年初存款数额,第五次的计算 结果即为题解*/ printf(\} *运行结果 He must save 4039.44 at first 14.怎样存钱利最大 假设银行整存整取存款不同期限的月息利率分别为: 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为对应存款年限的利率。根据题意还可得到以下限制条件: - 6 - 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 int i8,i5,i3,i2,i1,n8,n5,n3,n2,n1; made fixed deposit for 1 year: 0times Total:8841.01 可见最佳的存款方案为连续四次存5年期。 *思考题 某单位对职工出售住房,每套为2万元。买房付款的方法是: 一次交清,优惠20% 从第一年开始,每年年初分期付款: 5年交清,优惠50%; 10年交清,优惠10%; 20年交清,没有优惠。 现在有人手中正好有2万元,若假定在今后20年中物价和银行利率均保持不变,问他应当选择哪种付款方式可以使应付的钱最少? 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(\in a bank:\\n\ printf(\printf(\printf(\printf(\printf(\printf(\/*输出存款方式*/ } *运行结果 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 15.捕鱼和分鱼 A、B、C、D、E五个人在某天夜里合伙去捕鱼,到第二天凌晨时都疲惫不堪,于是各自找地方睡觉。日上三杆,A第一个醒来, 他将鱼分为五份,把多余的一条鱼扔掉,拿走自己的一份。B第二个醒来,也将鱼分为五份,把多余的一条鱼扔掉,保持走自己的一份。C、D、E依次醒来,也按同样的方法拿走鱼。问他们合伙至少捕了多少条鱼? *问题分析与算法设计 根据题意,总计将所有的鱼进行了五次平均分配,每次分配时的策略是相同的,即扔掉一条鱼后剩下的鱼正好分成五份,然后拿走自己的一份,余下其它的四份。 假定鱼的总数为X,则X可以按照题目的要求进行五次分配:X-1后可被5整除,余下的鱼为4*(X-1)、5。若X满足上述要求,则X就是题目的解。 *程序说明与注释 #include int n,i,x,flag=1; /*flag:控制标记*/ for(n=6;flag;n++) /*采用试探的方法。令试探值n逐步加大*/ { for(x=n,i=1&&flag;i<=5;i++) if((x-1)%5==0) x=4*(x-1)/5; else flag=0; /*若不能分配则置标记falg=0退出分配过程*/ if(flag) break; /*若分配过程正常结束则找到结果退出试探的过程*/ else flag=1; /*否则继续试探下一个数*/ } printf(\输出结果*/ } *运行结果 Total number of fish catched = 3121 - 7 - *问题的进一步讨论 程序采用试探法,试探的初值为6,每次试探的步长为1。这是过分保守的做法。可以在进一步分析题目的基础上修改此值,增大试探的步长值,以减少试探次数。 *思考题 请使用其它的方法求解本题。 16.出售金鱼 买卖提将养的一缸金鱼分五次出售系统上一次卖出全部的一半加二分之一条;第二次卖出余下的三分之一加三分之一条;第三次卖出余下的四分之一加四分之一条;第四次卖出余下的五分之一*思考题 日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完后父亲说:―老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的老六拿到后连同原先的桔子分1/3给老大‖。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子? 17.平分七筐鱼 甲、乙、丙三位鱼夫出海打鱼,他们随船带了21只箩筐。当晚返航时,他们发现有七筐装满了鱼,还有七筐装了半筐鱼,另外七筐则是空的,由于他们没有秤,只好通过目测认为七个满筐鱼桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六; 加五分之一条;最后卖出余下的11条。问原来的鱼缸中共有几条金鱼? *问题分析与算法设计 题目中所有的鱼是分五次出售的,每次卖出的策略相同;第j次卖剩下的(j+1)分之一再加1/(j+1)条。第五次将第四次余下的11条全卖了。 假定第j次鱼的总数为X,则第j次留下: x-(x+1)/(j+1) 当第四次出售完毕时,应该剩下11条。若X满足上述要求,则X就是题目的解。 应当注意的是:\应满足整除条件。试探X的初值可以从23开始,试探的步长为2,因为X的值一定为奇数。 *程序说明与注释 #include int i,j,n=0,x; /*n为标志变量*/ 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) /*若第四次余下11条则满足题意*/ { printf(\输出结果*/ n=1; /*控制退出试探过程*/ } } } *运行结果 There are 59 fishes at first. 的重量是相等的,7个半筐鱼的重量是相等的。在不将鱼倒出来的前提下,怎样将鱼和筐平分为三份? *问题分析与算法设计 根据题意可以知道:每个人应分得七个箩筐,其中有3.5筐鱼。采用一个3*3的数组a来表示三个人分到的东西。其中每个人对应数组a的一行,数组的第0列放分到的鱼的整筐数,数组的第1列放分到的半筐数,数组的第2列放分到的空筐数。由题目可以推出: 。数组的每行或每列的元素之和都为7; 。对数组的行来说,满筐数加半筐数=3.5; 。每个人所得的满筐数不能超过3筐; 。每个人都必须至少有1 个半筐,且半筐数一定为奇数 对于找到的某种分鱼方案,三个人谁拿哪一份都是相同的,为了避免出现重复的分配方案,可以规定:第二个人的满筐数等于第 一个人的满筐数;第二个人的半筐数大于等于第一个人的半筐数。*程序说明与注释 #include int i,j,k,m,n,flag; printf(\ for(i=0;i<=3;i++) /*试探第一个人满筐a[0][0]的值,满筐数不能>3*/ { a[0][0]=i; for(j=i;j<=7-i&&j<=3;j++) /*试探第二个人满筐a[1][0]的值,满筐数不能>3*/ { a[1][0]=j; if((a[2][0]=7-j-a[0][0])>3)continue; /*第三个人满筐数不能>3*/ if(a[2][0]=前一个人,以排除重复情况*/ for(k=1;k<=5;k+=2) /*试探半筐a[0][1]的值,半筐数为奇 - 8 - 数*/ { a[0][1]=k; for(m=1;m<7-k;m+=2) /*试探 半筐a[1][1]的值,半筐数为奇数*/ { a[1][1]=m; a[2][1]=7-k-m; for(flag=1,n=0;flag&&n<3;n++) /*判断每个人分到的鱼是 3.5筐,flag为满足题意的标记变量*/ 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]; /*计算应得到的空筐数量*/ *题目分析与算法设计 根据题意可知,满足条件的五位数的选择范围是10006、10016。。。99996。可设基础数i=1000,通过计算i*10+6即可得到欲选的数(i的变化范围是1000~999),再判断该数能否被3整除。 *程序说明与注释 #include long int i; int count=0; /*count:统计满足条件的五位数的个数*/ for(i=1000;i<9999;i++) else flag=0; /*不符合题意则置标记为0*/ if(flag) { printf(\–basket Empty\\n\for(n=0;n<3;n++) printf(\'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人) 18.有限5位数 个位数为6且能被3整除的五位数共有多少? if(!((i*10+6)%3)) /*判断所选的数能否被3整除*/ count++; /*若满足条件则计数*/ printf(\} *运行结果 count=2999 *思考题 求100到1000之间有多少个其数字之和为5的整数。 (答案:104,113,122,131,140,203,212,221,230, 302,311,320,401,410,500) 19.8除不尽的自然数 一个自然数被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 for(i=0;;i++) /*试探商的值*/ if(((i*8+7)*8+1)*8+1==(34*i+15)*17+4) { /*逆推判断所取得的当前i值是否满足关系式*/ /*若满足则输出结果*/ printf(\is: %d\\n\break; /*退出循环*/ - 9 - } } *运行结果 The required number is:1993 20.一个奇异的三位数 一个自然数的七进制表达式是一个三位数,而这个自然数的九进制表示也是一个三位数,且这两个三位数的数码正好相反,求这个三位数。 *问题分析与算法设计 根据题意可知,七进制和九进制表示的这全自然数的每一位一定*程序说明与注释 #include for(i=1002;i<1111;i++) /*穷举四位数可能的值*/ if(i*1000+i/10*100+i/100*10+i/1000==i*9) /*判断反序数是否是原整数的9倍*/ /*若是则输出*/ } *运行结果 printf(\ 小于7,可设其七进制数形式为kji(i、j、k的取值分别为1~6),然后设其九进制表示形式为ijk。 *程序说明与注释 #include 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(\ printf(\k,i*9*9+j*9+k); } } *运行结果 The special number with 3 digits is:503(7)=305(9)=248(10) C/C++语言经典、实用、趣味程序设计编程百例精解(3) 位反序数 设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。 The number satisfied states condition is:1089 22.求车速 一辆以固定速度行驶的汽车,司机在上午10点看到里程表上的读数是一个对称数(即这个数从左向右读和从右向左读是完全一样的),为95859。两小时后里程表上出现了一个新的对称数。问该车的速度是多少?新的对称数是多少? *问题分析与算法设计 根据题意,设所求对称数为i,其初值为95589,对其依次递增取值,将i值的每一位分解后与其对称位置上的数进行比较,若每个对称位置上的数皆相等,则可判定i即为所求的对称数。 *程序说明与注释 #include int t,a[5]; /*数组a存放分解的数字位*/ long int k,i; for(i=95860;;i++) /*以95860为初值,循环试探*/ { for(t=0,k=100000;k>=10;t++) /*从高到低分解所取i值的每位数*/ { /* 字,依次存放于a[0]~a[5]中*/ a[t]=(i%k)/(k/10); k/=10; } if((a[0]==a[4])&&(a[1]==a[3])) { printf(\is:%d%d%d%d%d\\n\a[0],a[1],a[2],a[3],a[4]); printf(\ break; } } } - 10 - *运行结果 The new symmetrical number kelometers is:95959. The velocity of the car is:50.00 *思考题 将一个数的数码倒过来所得到的新数叫原数的反序数。如果一个数等于它的反序数,则称它为对称数。求不超过1993的最大的二进制的对称数。 23.由两个平方三位数获得三个平方二位数 已知两个平方三位数abc和xyz,其中a、b、c、x、y、z未必所指向的数组中 ————————————————*/ void f(int n,float* s) { int k; for(k=1000;k>=10;++s) { *s = (n%k) /(k/10); k /=10; } } *运行结果 是不同的;而ax、by、cz是三个平方二位数。请编程求三位数abc和xyz。 *问题分析与算法设计 任取两个平方三位数n和n1,将n从高向低分解为a、b、c,将n1从高到低分解为x、y、z。判断ax、by、cz是否均为完全平方数。 *程序说明与注释 #include float a[3],b[3]; print(\are:\\n\ for(i=11;i<=31;++i) //穷举平方三位数的取值范围 for(t=11;t<=31;++t) { f(i*i,a); //分解平方三位数的各位,每位数字分别存入数组中 f(t*t,b); if(sqrt(a[0]*10+b[0]) == (int)sqrt(a[0]*10+b[0]) && sqrt(a[1]*10+b[1]) == (int)sqrt(a[1]*10+b[1]) && sqrt(a[2]*10+b[2]) == (int)sqrt(a[2]*10+b[2]) ) { printf(\若三个新的数均是完全平方数,则输出 } } } /* ———————————————- 分解三位数n的各位数字,将各个数字从高到低依次存入指针s The possible perfect squares combinations are: 400 and 900 841 and 196 *思考题 求这样一个三位数,该三位数等于其每位数字的阶乘之和。 即 abc = a! + b! + c! (正确结果:145 = 1! + 4! +5!) 24.阿姆斯特朗数 如果一个正整数等于其各个数字的立方和,则称该数为阿姆斯特朗数(亦称为自恋性数)。 如 407=43+03+73就是一个阿姆斯特朗数。试编程求1000以内的所有阿姆斯特朗数。 *问题分析与算法设计 可采用穷举法,依次取1000以内的各数(设为i),将i的各位数字分解后,据阿姆斯特朗数的性质进行计算和判断。 *程序说明与注释 #include int i,t,k,a[3]; printf(\than 1000:\\n\ for(i=2;i<1000;i++) /*穷举要判定的数i的取值范围2~1000*/ { for(t=0,k=1000;k>=10;t++) /*截取整数i的各位(从高向低位)*/ { a[t]=(i%k)/(k/10); /*分别赋于a[0]~a[2}*/ k/=10; - 11 - } if(a[0]*a[0]*a[0]+a[1]*a[1]*a[1]+a[2]*a[2]*a[2]==i) /*判断i是否为阿姆斯特朗数*/ printf(\若满足条件,则输出*/ } printf(\} *运行结果 There are following Armstrong number smaller than 1000: 153 370 371 407 25.完全数 *问题分析与算法设计 按照亲密数定义,要判断数a是否有亲密数,只要计算出a的全部因子的累加和为b,再计算b的全部因子的累加和为n,若n等于a则可判定a和b是亲密数。计算数a的各因子的算法: 用a依次对i(i=1~a/2)进行模运算,若模运算结果等于0,则i为a的一个因子;否则i就不是a的因子。 *程序说明与注释 #include int a,i,b,n; printf(\–numbers pair smaller than 3000:\\n\ 如果一个数恰好等于它的因子之和,则称该数为―完全数‖。 *问题分析与算法设计 根据完全数的定义,先计算所选取的整数a(a的取值1~1000)的因子,将各因子累加于m,若m等于a,则可确认a为完全数。 *程序说明与注释 #include int a,i,m; printf(\1000:\\n\ for(a=1;a<1000;a++) /*循环控制选取1~1000中的各数进行判断*/ { for(m=0,i=1;i<=a/2;i++) /*计算a的因子,并将各因子之和m=a,则a是完全数输出*/ if(!(a%i))m+=i; if(m==a) printf(\} printf(\} *运行结果 TThere are following perfect numbers smaller than 1000: 6 28 496 26.亲密数 如果整数A的全部因子(包括1,不包括A本身)之和等于B;且整数B的全部因子(包括1,不包括B本身)之和等于A,则将整数A和B称为亲密数。求3000以内的全部亲密数。 for(a=1;a<3000;a++) /*穷举1000以内的全部整数*/ { for(b=0,i=1;i<=a/2;i++) /*计算数a的各因子,各因子之和存放于b*/ if(!(a%i))b+=i; /*计算b的各因子,各因子之和存于n*/ for(n=0,i=1;i<=b/2;i++) if(!(b%i))n+=i; if(n==a&&a 输出*/ } } *运行结果 There are following friendly–numbers pair smaller than 3000: 220.. 284 1184.. 1210 2620.. 2924 27.自守数 自守数是指一个数的平方的尾数等于该数自身的自然数。例如: 252=625 762=5776 93762=87909376 请求出200000以内的自守数 *问题分析与算法设计 若采用―求出一个数的平方后再截取最后相应位数‖的方法显然是不可取的,因为计算机无法表示过大的整数。 分析手工方式下整数平方(乘法)的计算过程,以376为例: 376 被乘数 X 376 乘数 ———- 2256 第一个部分积=被乘数*乘数的倒数第一位 2632 第二个部分积=被乘数*乘数的倒数第二位 1128 第三个部分积=被乘数*乘数的倒数第三位 ———- 141376 积 本问题所关心的是积的最后三位。分析产生积的后三位的过程,可以看出,在每一次的部分积中,并不是它的每一位都会对积的 - 12 - 后三位产生影响。总结规律可以得到:在三位数乘法中,对积的后三位产生影响的部分积分别为: 第一个部分积中:被乘数最后三位*乘数的倒数第一位 第二个部分积中:被乘数最后二位*乘数的倒数第二位 第三个部分积中:被乘数最后一位*乘数的倒数第三位 将以上的部分积的后三位求和后截取后三位就是三位数乘积的后三位。这样的规律可以推广到同样问题的不同位数乘积。 按照手工计算的过程可以设计算法编写程序。 *程序说明与注释 #include long mul,number,k,ll,kk; *程序说明与注释 原程序好像有错,而且比较费解,现基于原程序修改如下(如果读者还发现错误请提出): #include int m[16],n,i,t,count=0; long unsigned a,k; printf(\for(n=1;n<256;n++) /*穷举n的取值范围*/ { k=0;t=1;a=n*n; /*计算n的平方*/ printf(\than 200000:\\n\ for(number=0;number<200000;number++) { for(mul=number,k=1;(mul/=10)>0;k*=10); /*由number的位数确定截取数字进行乘法时的系数k*/ kk=k*10; /*kk为截取部分积时的系数*/ mul=0; /*积的最后n位*/ ll=10; /*ll为截取乘数相应位时的系数*/ while(k>0) { mul=(mul+(number%(k*10))*(number%ll-number%(ll/10)))%kk; /*(部分积+截取被乘数的后N位*截取乘数的第M位),%kk再截取部分积*/ k/=10; /*k为截取被乘数时的系数*/ ll*=10; } if(number==mul) /*判断若为自守数则输出*/ printf(\} } *运行结果 It exsts following automorphic numbners smaller than 200000: 0 1 5 6 25 76 376 625 9376 90625 109376 28.回文数 打印所有不超过n(取n<256) 的其平方具有对称性质的数(也称回文数)。 *问题分析与算法设计 对于要判断的数n,计算出其平方后(存于a),将a的每一位进行分解,再按a的从低到高的顺序将其恢复成一个数k(如n=13,则a=169且k=961),若a等于k则可判定n为回亠数。 for(i=0;a!=0;i++) /*从低到高分解数a的每一位存于数组m[0]~m[16]*/ { m[i]=a;//这个是取得a的个位,整个循环合起来就可以取得各个位 a/=10; } int j=0; for(i–;j if(m[j]!=m[i])break;//只要有一位不是对称,那就说明不是对称,就可以退出了 //所有的位都对称就说明是对称了,这样就可以打印出结果了 if(j>=i)printf(\} return 0; } *运行结果 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 10 202 40804 11 212 44944 //下面程序是原来的,有错,而且费解 #include - 13 - { int m[16],n,i,t,count=0; long unsigned a,k; printf(\for(n=1;n<256;n++) /*穷举n的取值范围*/ { k=0;t=1;a=n*n; /*计算n的平方*/ for(i=1;a!=0;i++) /*从低到高分解数a的每一位存于数组m[1]~m[16]*/ { m[i]=a;//安安注:这个是取得a的个位,整个循环合起来就可以取得各个位,并存于数组中,为了是下面判断是不是对称 a/=10; *程序说明与注释 #include int n,a,b; condition\\n\ for(n=1000;n<10000;n++) /*四位数N的取值范围1000~9999*/ { a=n/100; /*截取N的前两位数存于a*/ b=n0; /*截取N的后两位存于b*/ if((a+b)*(a+b)==n) /*判断N是否为符合题目所规定的性质printf(\ } for(;i>1;i–) { k+=m[i-1]*t; t*=10; } if(k==n*n) printf(\} return 0; } *运行结果 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 29.求具有abcd=(ab+cd)2性质的四位数 3025这个数具有一种独特的性质:将它平分为二段,即30和25,使之相加后求平方,即(30+25)2,恰好等于3025本身。请求出具有这样性质的全部四位数。 *问题分析与算法设计 具有这种性质的四位数没有分布规律,可以采用穷举法,对所有四位数进行判断,从而筛选出符合这种性质的四位数。具体算法实现,可任取一个四位数,将其截为两部分,前两位为a,后两位为b,然后套用公式计算并判断。 的四位数*/ printf(\} } *运行结果 There are following numbers with 4 digits satisfied condition: 2025 3025 9801 30.求素数 求素数表中1~1000之间的所有素数 *问题分析与算法设计 素数就是仅能衩1和它自身整除的整数。判定一个整数n是否为素数就是要判定整数n能否被除1和它自身之外的任意整数整除,若都不能整除,则n为素数。 程序设计时i可以从2开始,到该整数n的1/2为止,用i依次去除需要判定的整数,只要存在可以整除该数的情况,即可确定要判断的整数不是素数,否则是素数。 *程序说明与注释 #include int n1,nm,i,j,flag,count=0; do{ printf(\ scanf(\输入求素数的范围*/ }while(!(n1>0&&n1 printf(\\\n\if(n1==1||n1==2) /*处理素数2*/ { printf(\n1=3;count++; } for(i=n1;i<=nm;i++) /*判定指定范围内的整数是否为素数*/ - 14 - { if(!(i%2))continue; for(flag=1,j=3;flag&&j /*判定能否被从3到整数的一半中的某一数所整除*/ if(!(i%j))flag=0; /*若能整除则不是素数*/ if(flag) printf(++count?\} } *思考题 请找出十个最小的连续自然数,它们个个都是合数(非素数) if(i<=1)return 0; if(i==2)return 1; if(!(i%2))return 0; /*if no,return 0*/ for(j=3;j<=(int)(sqrt((double)i)+1);j+=2) if(!(i%j))return 0; return 1; /*if yes,return 1*/ } 32.可逆素数 求四位的可逆素数。可逆素数指:一个素数将其各位数字的顺序倒过来构成的反序数也是素数。 C/C++语言经典、实用、趣味程序设计编程百例精解(4) 31.歌德巴赫猜想 验证:2000以内的正偶数都能够分解为两个素数之和(即验证歌德巴赫猜想对2000以内的正偶数成立)。 *问题分析与算法设计 为了验证歌德巴赫猜想对2000以内的正偶数都是成立的,要将整数分解为两部分,然后判断出分解出的两个整数是否均为素数。若是,则满足题意;否则重新进行分解和判断。 程序中对判断是否为素数的算法进行了改进,对整数判断―用从2开始到该整数的一半‖改为―2开始到该整数的平方根‖。原因何在请自行分析。 *程序说明与注释 #include for(i=4;i<=2000;i+=2) { for(n=2;n printf(\若均是素数则输出*/ break; } if(n==i) printf(\} } int fflag(int i) /*判断是否为素数*/ { int j; *问题分析与算法设计 本题的重点不是判断素数的方法,而是求一个整数的反序数。 求反序数的方法是从整数的末尾依次截取最后一位数字,每截取一次后整数缩小10倍,将截取的数字作为新的整数的最后一位(新的整数扩大10倍后加上被截取的数字)。这样原来的整数的数字从低到高被不断地截取,依次作为新的整数从高到低的各位数字。 *程序说明与注释 #include int i,count; printf(\for(count=0,i=1001;i<9999;i+=2) //穷举全部的奇数 { if(num(i)) //若是可逆素数,则输出 printf(count%9 ? \ } return 0; } int num(int number) { int i,j; if(!ok(number))return 0; //判断是否为素数 for(i=number,j=0;i>0;i/=10) //按位将整数倒过来,产生反序数 { j=j*10 + i; } if(number if(!ok(i)) //判断对应的反序数是否为可逆素数 - 15 - 爱因斯坦出了一道这样的数学题:有一条长阶梯,若每步跨2阶,则最最后剩一阶,若每步跨3 阶,则最后剩2阶,若每步跨5阶,则最后剩4阶,若每步跨6阶则最后剩5阶。只有每次跨7阶,最后才正好一阶不剩。请问这条阶梯共有多少阶? *问题分析与算法设计 根据题意,阶梯数满足下面一组同余式: x≡1 (mod2) x≡2 (mod3) x≡4 (mod5) x≡5 (mod6) x≡0 (mod7) *程序说明与注释 for(k=0;k<=100-i-2*j;k+=5) /*k为5分硬币钱数*/ if(i+j+k==100) printf(count%4?\d+2*%d+5*%d\\n\} 39.年龄几何 张三、李四、王五、刘六的年龄成一等差数列,他们四人的年龄相加是26,相乘是880,求以他们的年龄为前4项的等差数列的前20项。 *问题分析与算法设计 #include int i=1; /*i为所设的阶梯数*/ while(!((i%2==1)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==0))) ++i; /*满足一组同余式的判别*/ printf(\} *运行结果 Staris_number=119 *问题的进一步讨论 此题算法还可考虑求1、2、4、5的最小公倍数n,然后判t(t为n-1)≡0(mod7)是否成立,若不成立则t=t+n,再进行判别,直至选出满足条件的t值。请自行编写程序实现 38.换分币 用一元人民币兑换成1分、2分和5分硬币,共有多少种不同的兑换方法。 *问题分析与算法设计 根据题意设i,j,k分别为兑换的1分、2分、5分硬币所具有的钱数(分),则i,j,k的值应满足: i+j+k=100 *程序说明与注释 #include int i,j,k,count=1; printf(\Yuan note:\\n\ for(i=0;i<=100;i++) /*i为1分硬币钱数,可取值0,1,2…,100*/ for(j=0;j<=100-i;j+=2) /*j为2分硬币钱数,可取0值,2,4,…,100*/ 设数列的首项为a,则前4项之和为\前4 项之积为\。同时 \。可采用穷举法求出此数列。 *程序说明与注释 #include printf(\for(n=1;n<=6;n++) /*公差n取值为1~6*/ for(a=1;a<=4;a++) /*首项a取值为1~4*/ if(4*n+6*a==26&&n*(n+a)*(n+a+a)*(n+a+a+a)==880) /*判断结果*/ for(i=0;i<20;i++) printf(\输出前20项*/ } *运行结果 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 40.三色球问题 若一个口袋中放有12个球,其中有3个红的。3个白的和6个黒的,问从中任取8个共有多少种不同的颜色搭配? *问题分析与算法设计 设任取的红球个数为i,白球个数为j,则黒球个数为8-i-j,根据题意红球和白球个数的取值范围是0~3,在红球和白球个数确定的条件下,黒球个数取值应为8-i-j<=6。 *程序说明与注释 #include int i,j,count=0; printf(\ - 21 - printf(\\\n\ for(i=0;i<=3;i++) /*循环控制变量i控制任取红球个数0 ̄3*/ for(j=0;j<=3;j++) /*循环控制变量j控制任取白球个数0 ̄3*/ if((8-i-j)<=6) printf(\} C/C++语言经典、实用、趣味程序设计编程百例精解(5) 41.马克思手稿中的数学题 马克思手稿中有一道趣味数学问题:有30个人,其中有男人、女人和小孩,在一家饭馆吃饭花了50先令;每个男人花3先令,每*程序说明与注释 #include int a,b,num1,num2,temp; printf(\scanf(\ if(num1>num2) /*找出两个数中的较大值*/ { temp=num1; num1=num2; num2=temp; /*交换两个整数*/ } a=num1; b=num2; while(b!=0) /*采用辗转相除法求最大公约数*/ 个女人花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 int x,y,z,count=0; printf(\ printf(\\\n\for(x=0;x<=10;x++) { y=20-2*x; /*x定值据(3)式求y*/ z=30-x-y; /*由(1)式求z*/ if(3*x+2*y+z==50) /*当前得到的一组解是否满足式(2)*/ printf(\} } 42.最大公约数和最小公倍数 求任意两个正整数的最大公约数和(GCD)和最小公倍数(LCM) *问题分析与算法设计 手工方式求两个正整数的蝚大公约数的方法是用辗转相除法,在程序中可以模拟这种方式。 { temp=a%b; a=b; b=temp; } printf(\输出最大公约数*/ printf(\输出最小公倍数*/ } *运行结果 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 *思考题 求一个最小的正整数,这个正整数被任意n(2<=n<=10)除都是除不尽的,而且余数总是(n-1)。例如:被9除时的余数为8。要求设计一个算法,不允许枚举与除2、除3、?.、除9、除10有关的命令,求出这个正整数。 43.分数比较 比较两个分数的大小。 - 22 - *问题分析与算法设计 人工方式下比较分数大小最常用的方法是:进行分数的通分后比较分子的大小。可以编程模拟手式方式。 *程序说明与注释 #include int i,j,k,l,m,n; printf(\ scanf(\输入两个分数*/ m=zxgb(j,l)/j*i; /*求出第一个分数通分后的分子*/ n=zxgb(j,l)/l*k; /*求出第二个分数通分后的分子*/ printf(\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); /*求出s的值*/ if(!((p*q*r)%(p*q*r-q*r-p*r-p*q))&&s>=r) /*输出结果*/ } } *运行结果 printf(\ if(m>n) printf(\/*比较分子的大小*/ else if(m==n) printf(\输出比较的结果*/ else printf(\} int zxgb(int a,int b) { long int c; int d; if(a 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 44.分数之和 求这样的四个自然数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 采用最简单的穷举方法可以很方便的求解。 程序与程序注释: #include int p,q,r,s,count=0; *思考题 将1、2、3、4、5、6、7、8、9九个数字分成以下三种分数形式之一,每个数字只能用一次,使得该分数刚好等于一个整数。 求所有满足条件的表示形式。 (参考答案:某些自然数没有这种表示形式,如:1、2、3、4、15、 18等。此外整数100有11种满足条件的表示形式;89的表示形 式最多,共有36种;三种形式中,最大可表示的整数为794。) 45.将真分数分解为埃及分数 分子为1 的分数称为埃及分数,现输入一个真分数,请将该分数分解为埃及分数。 如:8/11=1/2+1/5+1/55+1/110。 *问题分析与算法设计 若真分数的分子a能整除分母b,则真分数经过化简就可以得到埃及分数,若真分数的分子不能整除分母,则可以从原来的分数中分解出一个分母为b/a+1的埃及分数。用这种方法将剩余部分反复分解,最后可得到结果。 *程序说明与注释 /*安安注:对源程序作稍许修改,主要是添加了一个外循环,可以直接计算多个真分数的埃及分数,按Ctrl-C退出。具体的算法我没有认真看,有问题请提出,谢谢*/ #include long int a,b,c; while(true) { printf(\scanf(\输入分子a和分母b*/ printf(\while(true) { if(b%a) /*若分子不能整除分母*/ c=b/a+1; /*则分解出一个分母为b/a+1的埃及分数*/ - 23 - else{ c=b/a; a=1;} /*否则,输出化简后的真分数(埃及分数)*/ if(a==1) { printf(\break; /*a为1标志结束*/ } else printf(\a=a*c-b; /*求出余数的分子*/ b=b*c; /*求出余数的分母*/ if(a==3) /*若余数为3,输出最后两个埃及分数*/ { printf(\} while(num2!=0) /*采用辗转相除法求出最大公约数*/ { temp=num1%num2; num1=num2; num2=temp; } if(num1==1) /*若最大公约数为1,则为最简真分数*/ printf(\} } *运行结果 The fraction serials with demominator 40 is: } return 0; } *运行结果 Please enter a optional fraction (a/b): 1/6 It can be decomposed to: 1/6 Please enter a optional fraction (a/b): 20/33 It can be decomposed to: 1/2+1/10+1/165 Please enter a optional fraction (a/b): 10/89 It can be decomposed to: 1/9+1/801 Please enter a optional fraction (a/b): 19/99 It can be decomposed to: 1/6+1/40+1/3960 Please enter a optional fraction (a/b): 8/87 It can be decomposed to: 1/11+1/957 ??(按ctrl-c退出) 46.列出真分数序列 按递增顺序依次列出所有分母为40,分子小于40的最简分数。 *问题分析与算法设计 对分子采用穷举法,利用最大公约数的方法,判断分子与40是否构成真分数。 *程序说明与注释 #include int i,num1,num2,temp; printf(\for(i=1;i<=40;i++) /*穷举40以内的全部分子*/ { num1=40; num2=i; 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的最简真分数 47.计算分数的精确值 使用数组精确计算M/N(0 由于计算机字长的限制,常规的浮点运算都有精度限制,为了得到高精度的计算结果,就必须自行设计实现方法。 为了实现高精度的计算,可将商存放在一维数组中,数组的每个元素存放一位十进制数,即商的第一位存放在第一个元素中,商的第二位存放在第二个元素中?.,依次类推。这样就可以使用数组不表示一个高精度的计算结果。 进行除法运算时可以模拟人的手工操作,即每次求出商的第一位后,将余数乘以10,再计算商的下一位,重复以上过程,当某次计算后的余数为0 时,表示M/N为有限不循环小数某次计算后的余数与前面的某个余数相同时,则M/N为无限循环小数,从该余数第一次出现之后所求得的各位数就是小数的循环节。 程序具体实现时,采用了数组和其它一些技巧来保存除法运算所得到的余数和商的各位数。 *程序说明与注释 #include int remainder[101],quotient[101]; /*remainder:存放除法的余数; quotient:依次存放商的每一位*/ int main() { int m,n,i,j; printf(\scanf(\输入被除数和除数*/ printf(\for(i=1;i<=100;i++) /*i: 商的位数*/ { - 24 - remainder[m]=i; /*m:除的余数 remainder[m]:该余数对应的商的位数*/ m*=10; /*余数扩大10位*/ quotient[i]=m/n; /*商*/ m=m%n; /*求余数*/ if(m==0) /*余数为0 则表示是有限小数*/ { for(j=1;j<=1;j++) printf(\输出商*/ break; /*退出循环*/ } if(remainder[m]!=0) /*若该余数对应的位在前面已经出现过*/ { for(j=1;j<=i;j++) printf(\则输出循环{ int x,y,z; for(x=1;x<=3;x++) /*穷举x的全部可能配偶*/ for(y=1;y<=3;y++) /*穷举y的全部可能配偶*/ for(z=1;z<=3;z++) /*穷举z的全部可能配偶*/ if(x!=1&&x!=3&&z!=3&&x!=y&&x!=z&&y!=z) /*判断配偶是否满足题意*/ { printf(\printf(\} } printf(\打印判断结果*/ 小数*/ printf(\from %d\\n\ printf(\/*输出循环节的位置*/ break; /*退出*/ } } } *思考题 使用数组实现计算MXN的精确值 48.新娘和新郞 三对情侣参加婚礼,三个新郞为A、B、C,三个新娘为X、Y、Z。有人不知道谁和谁结婚,于是询问了六位新人中的三位,但听到的回答是这样的:A说他将和X结婚;X说她的未婚夫是C;C说他将和Z结婚。这人听后知道他们在开玩笑,全是假话。请编程找出谁将和谁结婚。 *问题分析与算法设计 将A、B、C三人用1,2,3表示,将X和A结婚表示为“X=1”,将Y不与A结婚表示为“Y!=1”。按照题目中的叙述可以写出表达式: x!=1 A不与X结婚 x!=3 X的未婚夫不是C z!=3 C不与Z结婚 题意还隐含着X、Y、Z三个新娘不能结为配偶,则有: x!=y且x!=z且y!=z 穷举以上所有可能的情况,代入上述表达式中进行推理运算,若假设的情况使上述表达式的结果均为真,则假设情况就是正确的结果。 *程序说明与注释 #include *运行结果 X will marry to B. (X与B结婚) Y will marry to C. (Y与C结婚) Z will marry to A. (Z与A结婚) 49.委派任务 某侦察队接到一项紧急任务,要求在A、B、C、D、E、F六个队员中尽可能多地挑若干人,但有以下限制条件: 1)A和B两人中至少去一人; 2)A和D不能一起去; 3)A、E和F三人中要派两人去; 4)B和C都去或都不去; 5)C和D两人中去一个; 6)若D不去,则E也不去。 问应当让哪几个人去? *问题分析与算法设计 用A、B、C、D、E、F六个变量表示六个人是否去执行任务的状态, 变量的值为1,则表示该人去;变量的值为0,则表示该人不参加执行任务,根据题意可写出表达式: a+b>1 A和B两人中至少去一人; a+d!=2 A和D不能一起去; a+e+f==2 A、E、F三人中要派两人去; b+c==0或b+c==2 B和C都去或都不去; c+d==1 C和D两人中去一个; d+e==0或d==1 若D不去,则E也不去(都不去;或D去E随便)。上述各表达式之间的关系为“与”关系。穷举每个人去或不去的各种可能情况,代入上述表达式中进行推理运算,使上述表达式均为“真”的情况就是正确的结果。 *程序说明与注释 #include int a,b,c,d,e,f; - 25 - 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号吉林 &&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(\for(j=1;j<=3;j++) { printf(\输出各家孩子的得分情况*/ if(score[i][j]==1)who=i; /*记录最后一名的家庭序号*/ } E 答:2号甘肃,3号陕西 57.谁家孩子跑最慢 张王李三家各有三个小孩。一天,三家的九个孩子在一起比赛短跑,规定不分年龄大小,跑第一得9分,跑第2得8分,依此类推。比赛结果各家的总分相同,且这些孩子没有同时到达终点的,也没有一家的两个或三个孩子获得相连的名次。已知获第一名的是李家的孩子,获得第二的是王家的孩子。问获得最后一名的是谁家的孩子? *问题分析与算法设计 按题目的条件,共有1+2+3+…+9=45分,每家的孩子的得分应为15分。根据题意可知:获第一名的是李家的孩子,获第二名的是王家的孩子,则可推出:获第三名的一定是张家的孩子。由―这些孩子没有同时到达终点的‖可知:名次不能并列,由―没有一家的两个或三个孩子获得相连的名次‖可知:第四名不能是张家的孩子。 程序中为了方便起见,直接用分数表示。 *程序说明与注释 #include int i,j,k,who; score[1][1]=7; /*按已知条件进行初始化:score[1]:张家三个孩子的得分*/ score[2][1]=8; /*score[2]:王家三个孩子的得分*/ score[3][1]=9; /*李家三个孩子的得分*/ for(i=4;i<6;i++) /*i:张家孩子在4到6分段可能的分数*/ for(j=4;j<7;j++) /*j:王家孩子在4到6分段可能的分数*/ for(k=4;i!=j&&k<7;k++) /*k:李家孩子在4到6分段可能的分数*/ 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] if(who==1) /*输出最后判断的结果*/ printf(\ Zhang.\\n\else if(who==2) printf(\ Wang.\\n\ else printf(\family Li.\\n\} *运行结果 7 5 3 8 6 1 9 4 2 The last one arrived to end is a child from family Wang. (获得最后一名的是王家的孩子。 58.拉丁方阵 构造 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 int i,j,k,t; - 31 - printf(\for(j=0;j for(i=0;i t=(i+j)%N; /*确定该拉丁方阵第i 行的第一个元素的值*/ for(k=0;k printf(\} } int count; /*计数器*/ int main() { static int a[]={1,2,3,4,5,6}; /*初始化数组*/ printf(\are:\\n\ for(a[1]=a[0]+1;a[1]<=5;++a[1]) /*a[1]必须大于a[0]*/ for(a[2]=a[1]+1;a[2]<=5;++a[2]) /*a[2]必须大于a[1]*/ for(a[3]=a[0]+1;a[3]<=5;++a[3]) /*第二行的a[3]必须大于a[0]*/ for(a[4]=a[1]>a[3]?a[1]+1:a[3]+1;a[4]<=5;++a[4]) *运行结果 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 59.填表格 将1、2、3、4、5和6 填入下表中,要使得每一列右边的数字比左边的数字大,每一行下面的数字比上面的数字大。按此要求,可有几种填写方法? *问题分析与算法设计 按题目的要求进行分析,数字1一定是放在第一行第一列的格中,数字6一定是放在第二行第三列的格中。在实现时可用一个一维数组表示,前三个元素表示第一行,后三个元素表示第二行。先根据原题初始化数组,再根据题目中填 写数字的要求进行试探。 *程序说明与注释 #include /*第二行的a[4]必须大于左侧a[3]和上边a[1]*/ 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[i]) return 0; /*若数组中的数字有重复的,返回0*/ return 1; /*若数组中的数字没有重复的,返回1*/ } void print(int u[]) { int k; printf(\for(k=0;k<6;k++) if(k%3==0) /*输出数组的前三个元素作为第一行*/ printf(\ else /*输出数组的后三个元素作为第二行*/ printf(\} *运行结果 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 60.1~9分成1:2:3的三个3位数 将1到9 这九个数字分成三个3位数,分求第一个3位数,正好是第二个3位数的二倍,是第三个3位数的三倍。问应当怎样分法。 *问题分析与算法设计 问题中的三个数之间是有数学关系的,实际上只要确定第一个三 - 32 - 位数就可以解决问题。 试探第一个三位数之后,计算出另外两个数,将其分别分解成三位数字,进行判断后确定所试探的数是否就是答案。 需要提醒的是:试探的初值可以是123,最大值是333。因为不可能超出该范围。 *程序与程序设计 #include int m,count=0; for(m=123;m<=333;m++) /*试探可能的三位数*/ C/C++语言经典、实用、趣味程序设计编程百例精解(7) 61.1~9组成三个3位的平方数 将1、2、3、4、5、6、7、8、9九个数字分成三组,每个数字只能用一次,即每组三个数不允许有重复数字,也不许同其它组的三个数字重复,要求每组中的三位数都组成一个平方数。 *问题分析与算法设计 本问题的思路很多,这里介绍一种简单快速的算法。 首先求出三位数中不包含0且是某个整数平方的三位数,这样的三位数是不多的。然后将满足条件的三位数进行组合,使得所选if(ok(m,a)&&ok(2*m,a+3)&&ok(3*m,a+6)) /*若满足题意*/ printf(\/*输出结果*/ } int ok(int t,int *z) /*分解t的值,将其存入z指向的三个数组元素,若满足要求返回1*/ { int *p1,*p2; for(p1=z;p1 *p1=t; /*分解整数*/ t/=10; for(p2=a;p2 if(*p1==0||*p2==*p1)return 0; /*若重复则返回*/ } return 1; /*否则返回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种可能的组合) 出的3个三位数的9个数字没有重复。 程序中可以将寻找足条件的三位数的过程和对该三位数进行数字分解的过程结合起来。 *程序说明与注释 #include int a[20],num[20][3],b[10]; /*a:存放满足条件的三位数*/ /*若不是10 的倍数,则分解三位数*/ /*分解该三位数中的每一个数字*/ int i,j,k,m,n,t,flag; printf(\ for(j=0,i=11;i<=31;i++) /*求出是平方数的三位数*/ if(i!=0) /*若不是10的倍数,则分解三位数*/ { k=i*i; /*分解该三位数中的每一个数字*/ num[j+1][0]=k/100; /*百位*/ num[j+1][1]=k/10; /*十位*/ num[j+1][2]=k; /*个位*/ 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; /*j:计数器,统计已找到的满足要求的三位数*/ } for(i=1;i<=j-2;++i) /*从满足条件的三位数中选出三个进行组合*/ { b[1]=num[i][0]; b[2]=num[i][1]; b[3]=num[i][2]; for(t=i+1;t<=j-1;++t) { b[4]=num[t][0]; /*取第t个数的三位数字*/ b[5]=num[t][1]; - 33 - b[6]=num[t][2]; for(flag=0,m=1;!flag&&m<=3;m++) /*flag:出现数字重复的标记*/ for(n=4;!flag&&n<=6;n++) /*判断两个数的数字是否有重复*/ if(b[m]==b[n])flag=1; /*flag=1:数字有重复*/ if(!flag) for(k=t+1;k<=j;k++) { b[7]=num[k][0]; /*取第k个数的三位数字*/ b[8]=num[k][1]; b[9]=num[k][2]; for(flag=0,m=1;!flag&&m<=6;m++) /*判断前两个数字for(i=1;i<=8;i++) /*输入个数*/ { printf(\scanf(\ii+=a[i]; } printf(\****\\n\ if(ii%2) /*和为奇数则输入的8个数不可用*/ { printf(\cube!\\n\exit(0); 是否*/ for(n=7;!flag&&n<=9;n++) /*与第三个数的数字重复*/ if(b[m]==b[n])flag=1; if(!flag) /*若均不重复则打印结果*/ printf(\} } } } *运行结果 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个。) 62.由8个整数形成奇特的立方体 任意给出8个整数,将这8个整数分别放在一个立方体的八个顶点上,要求每个面上的四个数之和相等。 *问题分析与算法设计 简化问题:将8个顶点对应数组中的8个元素,将―每个面上的四个数之和皆相等‖转换为数组无素之间和的相等关系。这里的关键在于正确地将立方体的8个顶点与数组的8个元素对应。 可以利用简单的穷举方法建立8个数的全部排列。 *程序说明与注释 #include int a[9],ii=0,i,a1,a2,a3,a4,b1,b2,b3,b4,flag; } for(flag=0,a1=1;a1<=8;a1++) /*flag:完成标记.flag=1;表示完成*/ 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; /*满足条件则将flag置1后退出*/ } out: if(flag) { printf(\follow:\\n\ printf(\\\n\printf(\\\n\printf(\printf(\ - 34 - printf(\printf(\\\n\ printf(\\\n\} else printf(\cube!\\n\} *运行结果 Please enter [1] number:20 Please enter [2] number:45 Please enter [3] number:39 Please enter [4] number:25 Please enter [5] number:29 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(\printf(\printf(\\\n\printf(\} } *运行结果 PEAR 1098 - ARA - 989 Please enter [6] number:7 Please enter [7] number:3 Please enter [8] number:2 Sorry they can't be constructed required cube! *思考题 程序中建立全排列的方法效率太低,算法虽然简单但程序过于冗余。请读者自行设计新的算法完成同样的工作。 63.减式还原 编写程序求解下式中各字母所代表的数字,不同的字母代表不同的数字。 PEAR - ARA ——– PEA *问题分析与算法设计 类似的问题从计算机算法的角度来说是比较简单的,可以采用最常见的穷举方法解决。程序中采用循环穷举每个字母所可能代表的数字,然后将字母代表的数字转换为相应的整数,代入算式后验证算式是否成立即可解决问题。 *程序说明与注释 #include int p,e,a,r; for(p=1;p<=9;p++) /*从1到9穷举字母p的全部可能取值*/ for(e=0;e<=9;e++) /*从0到穷举字母e的全部可能取值*/ if(p!=e) /*p不等于e*/ for(a=1;a<=9;a++) /*从0到9穷举字母a的全部可能取值*/ if(a!=p&&a!=e) for(r=0;r<=9;r++) /*从0到9穷举字母r的全部可能取值*/ ———- —— PEA 109 *思考题 请复原下面的和式。不同的字母代表不同的数字。 SEVEN 82524 82526 THREE 19722 19722 + TWO 答案: + 106 + 104 ———- ———– ———– TWELVE 102352 102352 64.乘式还原 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 void print(long a,long b,long s1,long s2,long s3); int jud(long q,char *pflag); int main() { long i,j,k,l,m,n,term,t1,t2,t3; - 35 - 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,\判断第三个部分积*/ if(flag) { for(flag=0,m=0;m<4&&!flag;) /*乘数的第二位*/ flag=jud((t2=++m*10*term)/10,\判断第二个相同时,返回1*/ return 1; else return 0; } *运行结果 3 7 2 × 2 4 6 ———- 2 2 3 2 1 4 8 8 7 4 4 ———— 部分积*/ if(flag) { for(flag=0,l=5;l<9&&!flag;) /*乘数的第三位*/ flag=jud(t1=++l*term,\判断第一个部分积*/ if(flag&&jud(t1+t2+t3,\判断乘式的积*/ print(term,n*100+m*10+l,t1,t2,t3); } } } } void print(long a,long b,long s1,long s2,long s3) /*打印结果*/ { printf(\printf(\printf(\\\n\ printf(\printf(\\\n\printf(\} int jud(long q,char *pflag) /*判断一个数的每一位是否满足要求的判断函数*/ /*q:需要判断的数。pflag:标志字符串,A用1表示,Z用0表示。标志串排列顺序:个十百…*/ { while(q!=0&&*pflag!=NULL) /*循环判断对应位的取值范围是否正确*/ if(*pflag-'0'!=(q>=5?1:0)) /*标志位与对应的位不符,返回0*/ return 0; else { q/=10;++pflag; /*若相符则取下一位进行判断*/ } if(q==0&&*pflag==NULL) /*q的位数与标志字符串的长度 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 65.乘式还原(2) 有乘法算式如下: ○○○ × ○○ ———— ○○○○ ○○○○ ———— ○○○○○ 18个○的位置上全部是素数(1、3、5或7),请还原此算式。 *问题分析与算法设计 问题中虽然有18数位,但只要确定乘数和被乘数后经过计算就可确定其它的数位。 乘数和被乘数共有5个数位,要求每个数都是质数。完全可以采用穷举的方法对乘数和被乘数进行穷举,经过判断后找出答案。但是这种方法给人的感觉是―太笨了‖,因为组成的数字只是质数(4个),完全没有必要在那么大的范围内进行穷举,只需要试探每一位数字为质数时的情况即可。 采用五重循环的方法实现对于5个数字的穷举,前面的许多例题中都已见过。循环实现简单易行,但嵌套的层次太多,需要穷举的变量的数量直接影响到循环嵌套的层数,这种简单的实现方法缺少技巧性。本例的程序中给出了另外一种同样功能的算法,该 - 36 - 算法的实现思想请阅读程序。 程序中并没有直接对质数进行穷举,而是将每个质数与1到4顺序一一对应,在穷举时为处理简单仅对1到4进行穷举处理,待要判断产生的乘积是否满足条件时再利用一个数组完成向对应质数的转换。请体会程序中的处理方法。程序中使用的算法实际上是回朔法。 *程序说明与注释 #include #define NUM 5 /*需要穷举的变量数目*/ #define C_NUM 4 /*每个变量的值的变化范围*/ int a[NUM+1]; /*为需要穷举的变量开辟的数组*/ /*a[1]:被乘数的百位,a[2]:十位,aa[3]:个位 a[4]:被乘数的十位 a[5]:个位*/ /*判断乘式的积是否满足题目条件*/ { printf(\若满足题意,则打印结果*/ printf(\printf(\\\n\printf(\printf(\printf(\\\n\printf(\} i=NUM; /*为穷举下一个可能取值作准备*/ } } int b[]={0,2,3,5,7}; /*存放质数数字的数组,不使用第0号元素*/ int f(long sum); int main() { int i,not_finish=1; i=2; /*i:将要进行处理的元素的指针下标。设置初始值*/ a[1]=1; /*为第1号元素设置初始值*/ while(not_finish) /*not_finish:程序运行没结束标记*/ { while(not_finish&&i<=NUM) /*处理包括第i个元素在内的后续元素,找出当前条件下的一种各个变量的一种可能的取值方法*/ if(a[i]>=C_NUM) /*当要处理的元素取超过规定的C_NUM时*/ if(i==1&&a[1]==C_NUM) not_finish=0; /*若1号元素已经到C_NUM,则处理全部结束*/ else a[i–]=0; /*将要处理的元素置0,下标-1(回退一个元素)*/ else a[i++]++; /*当前元素值加1后下标指针加1*/ if(not_finish) { long int sum1,sum2,sum3,sum4; /*定义临时变量*/ sum1=b[a[1>*100+b[a[2>*10+b[a[3>; /*计算被乘数*/ /*利用数组的下标与质数的对应关系完成序号1到4向质数的转换*/ 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)) } int f(long sum) /*判断sum的每一位数字是否是质数,若不是返回0,若是返回1*/ { int i,k,flag; /*flag=1:数字是质数的标记*/ while(sum>0) { i=sum; /*取个位的数字*/ 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 - 37 - ○○○B 2 2 8 8 ————- —————- ○○○○○○ 2 3 6 2 3 6 66.除式还原(1) 给定下列除式,其中包含5个7,其它打×的是任意数字,请加以还原。 × 7 × ————–商 ————– 除数——××| ×××××————-被除数 ×7 7 ————– × 7 × /*商的第一位与除数的积的后两位为77*/ printf(\} *运行结果 51463/53=971。 可以看作为下列算式: 9 7 1 ————- 5 3| 5 1 4 6 3 4 7 7 ————- 3 7 6 × 7 × ———- × × × × ———- ○ *问题分析与算法设计 首先分析题目,由除式本身尽可能多地推出已知条件。由除式本身书已知: 1、被除数的范围是10000到99999,除数的范围是10到99,且可以整除; 2、商为100到999之间,且十位数字为7; 3、商的第一位与除数的积为三位数,且后两位为77; 4、被除数的第三位一定为4; 5、 7乘以除数的积为一个三位数,且第二位为7; 6、商的最后一位不能为0,且与除数的积为一个二位数。 由已知条件就可以采用穷举的方法找出结果。 *程序说明与注释 #include long int i; int j,l; for(i=10000;i<=99999;i++) /*1. i:被除数*/ if(i00-i0==400) /*4. 被除数的第三位一定为4*/ for(j=10;j<=99;j++) /*1. j: 余数*/ if(i%j==0&&(l=i/j)0>=70&&l0<80&&l!=0&&l>100&&l<=999) /*1. 可以整除&& 2.商l在100到999之间且十位数字为7&&6.商的个数不能为0*/ if((j*(l))<100&&j*(l)>10) /*6. 商的个数与除数的积为二位数*/ if(j*70>=70&&j*70<80) /*5. 7乘以除数的积的第二位为7*/ if(j*(l/100)0==77&&j*(l/100)>100) 3 7 1 ———– 5 3 5 3 ———– ○ *问题的进一步讨论 在推出的已知条件中,几所有的条件都是十分明显的,换句话说, 推出的已知条件就是对题目的平铺直叙。这种推已知条件的方法十分简单,并且行之有效。 *思考题 下列除式中仅给定了一个8,其它打×的位置上是任意数字,请还原。 × 8 × —————-商 —————- 除数——-×××| ××××××—————被除数 ×××× ————— ××× ××× ————— ×××× ×××× ————— ○ 67.除式还原(2) 下列除式中仅在商中给定了一个7,其它打×的位置全部是任意数字,请还原。 ×7××× ————-商 —————— 除数 ——————-×××| ×××××××× ————-被除数 ×××× ————-1) - 38 - ————— ××× ————-2) ××× ————-3) ————— ×××× ————-4) ××× ————-5) —————– ×××× ————-6) ×××× ————-7) —————– 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] 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) 则难于求出除式中各部分的值。 对除式进行分析,改可能多地推出限制条件: 由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]1000 5)式部分积: 100 int main() { int a[4],b,c[3],d[4],i=1; for(a[0]=1010;a[0]<=1377;a[0]++) { printf(\ printf(\); printf(\ printf(\} } *运行结果: No 1:12128316/124=97809 *思考题 下列除式中―×‖所在的位置全部是任意数字,请还原。 ××××× ——————- ××× | ×××××××× ×××× —————— ×××× ××× ————— ××× ××× ———– ×××× ×××× ———– 0 68.九位累进可除数 求九位累进可除数。所谓九位累进可除数就是这样一个数:这个数用到1到9这九个数字组成,每个数字刚好只出现一次。这九个位数的前两位能被2整除,前三位能被3整除……前N位能被N整除,整个九位数能被9整除。 *问题分析与算法设计 问题本身可以简化为一个穷举问题:只要穷举每位数字的各种可 - 39 - 能取值,按照题目的要求对穷举的结果进行判断就一定可以得到正确的结果。 问题中给出了―累进可除‖这一条件,就使得我们可以在穷举法中加入条件判断。在穷举的过程中,当确定部分位的值后,马上就判断产生的该部分是否符合―累进可除‖条件,若符合,则继续穷举下一位数字;否则刚刚产生的那一位数字就是错误的。这样将条件判断引入到穷举法之中,可以尽可能早的发现矛盾,尽早地放弃不必要穷举的值,从而提高程序的执行效率。 为了达到早期发现矛盾的目的,不能采用多重循环的方法实行穷举,那样编出的程序质量较差。程序中使用的算法不再是穷举法,而是回朔法。 *程序说明与注释 #include else /*第i位已经满足要求,处理第i+1位*/ if(++i<=NUM) /*i+1处理下一元素,当i没有处理完毕时*/ if(a[i-1]==NUM) a[i]=1; /*若i-1的值已为NUM,则a[i]的值为1*/ else a[i]=a[i-1]+1; /*否则,a[i]的初值为a[i-1]值的\下一个\值*/ } if(not_finish) { printf(\for(k=1;k<=NUM;k++) /*输出计算结果*/ #define NUM 9 int a[NUM+1]; int main() { int i,k,flag,not_finish=1; long sum; i=1; /*i:正在处理的数组元素,表示前i-1个元素已经满足要求,正处理的是第i个元素*/ a[1]=1; /*为元素a[1]设置初值*/ while(not_finish) /*not_finish=1:处理没有结束*/ { while(not_finish&&i<=NUM) { for(flag=1,k=1;flag&&k if(a[k]==a[i])flag=0; /*判断第i个元素是否与前i-1个元素重复*/ for(sum=0,k=1;flag&&k<=i;k++) { sum=10*sum+a[k]; if(sum%k)flag=0; /*判断前k位组成的整数是否能被k整除*/ } if(!flag) /*flag=0:表示第i位不满足要求,需要重新设置*/ { if(a[i]==a[i-1]) /*若a[i]的值已经经过一圈追上a[i-1]*/ { i–; /*i值减1,退回处理前一个元素*/ if(i>1&&a[i]==NUM) a[i]=1; /*当第i位的值达到NUM时,第i位的值取1*/ else if(i==1&&a[i]==NUM) /*当第1位的值达到NUM时结束*/ not_finish=0; /*置程序结束标记*/ else a[i]++; /*第i位的值取下一个,加1*/ } else if(a[i]==NUM) a[i]=1; printf(\ if(a[NUM-1] *运行结果 The progressire divisible number is: 381654729 *思考题 求N位累进可除数。用1到9这九个数字组成一个N(3<=N<=9)位数,位数字的组成不限,使得该N位数的前两位能被2整除, 前3位能被3整除,……,前N位能被N整除。求满足条件的N位数。 69.魔术师的猜牌术(1) 魔术师利用一副牌中的13张黑桃,预先将它们排好后迭在一起,牌面朝下。对观众说:我不看牌,只数数就可以猜到每张牌是什么,我大声数数,你们听,不信?你们就看。魔术师将最上面的那张牌数为1,把它翻过来正好是黑桃A,将黑桃A放在桌子上, 然后按顺序从上到下数手上的余牌,第二次数1、2,将第一张牌放在这迭牌的下面,将第二张牌翻过来,正好是黑桃2,也将它放在桌子上,第三次数1、2、3,将前面两张依次放在这迭牌的下面,再翻第三张牌正好是黑桃3。这样依次进行将13张牌全翻出来,准确无误。问魔术师手中的牌原始顺序是怎样安排的? *问题分析与算法设计 题目已经将魔术师出牌的过程描述清楚,我们可以利用倒推的方法,很容易地推出原来牌的顺序。 人工倒推的方法是:在桌子上放13空盒子排成一圈,从1开始顺序编号,将黑桃A放入1号盒子中,从下一个空盒子开始对空的盒子计数,当数到第二个空盒子时,将黑桃2放入空盒子中, 然后再从下一个空盒子开始对空盒子计数,顺序放入3、4、5…, - 40 -
正在阅读:
趣味程序设计编程百例精解06-15
散文:在五月的天里03-30
洛阳耐火厂实习报告03-30
橡皮的自述作文600字07-01
融资租赁项目尽职调查报告 - 图文11-14
医学生网02-17
子宫颈癌预防的现代策略-郎景和07-20
一年级下册英语教案Module8Unit1Therex27sapairofshortsunderth04-05
学校远程教育年度工作总结例文精编多篇04-26
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 程序设计
- 趣味
- 编程
- 误差理论与测量平差基础习题集1
- 自动化机械手液压系统设计
- 《有机化学》复习资料-李月明
- 四年级(齐鲁书社版)传统文化教案
- 机械手1
- 2019高考语文二轮培优江苏专用文档:第三部分 专题三 论述类文本
- 技术部管理办法
- 第一部分 煤矿安全法律法规
- 2.4.2《情绪的管理》说课稿
- 教师学宪法心得体会(多篇)
- 煤矿生产实习报告
- 泰州市姜堰区2016-2017年第一学期九年级语文期中试题及答案
- 实验3 SQL的高级查询
- MATLAB教程课后实验报告题目及解答第一至第五章
- 2018全国各地高考物理模拟试题《动量守恒定律》试题汇编(含答案
- 2018版《基础》第一章:“人生的青春之问”教案
- 法律与会计的结合
- 早餐配送创业计划书
- 四方保护定值说明 - 图文
- 山东省济南一中2015届高三一模物理试题