目錄:
1.繪製餘弦曲線
在屏幕上用“*”顯示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”);
}
}
2.繪製餘弦曲線和直線
在屏幕上顯示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(“+”);
elseif(x==n) printf(“+”);
elseif(x==m||x==62-m) printf(“*”);
else printf(“”);
printf(“\\n”);
}
}
——————————————————————————–
3.繪製圓
在屏幕上用“*”畫一個空心的圓
*問題分析與算法設計
打印圓可利用圖形的左右對稱性。根據圓的方程:
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”);
}
}
4.歌星大獎賽
在歌星大獎賽中,有10個評委爲參賽的選手打分,分數爲1~100分。選手最後得分爲:去掉一個最高分和一個最低分後其餘8個分數的平均值。請編寫一個程序實現。
*問題分析與算法實現
這個問題的算法十分簡單,但是要注意在程序中判斷最大、最小值的變量是如何賦值的。
*程序說明與註釋
#include<stdio.h>
void main()
{
intinteger,i,max,min,sum;
max=-32768;
min=32767;
sum=0;
for(i=1;i<=10;i++)
{
printf(“Inputnumber %d=”,i);
scanf(“%d”,&integer);
sum+=integer;
if(integer>max)max=integer;
if(integer<min)min=integer;
}
printf(“Canceledmax score:%d\\nCanceled min score:%d\\n”,max,min);
printf(“Averagescore:%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 maxscore:99
Canceled minscore:90
Averagescore:92
*思考題
題目條件不變,但考慮同時對評委評分進行裁判,即在10個評委中找出最公平(即評分最接返平均分)和最不公平(即與平均分的差距最大)的評委,程序應該怎樣實現?
——————————————————————————–
5.求最大數
問555555的約數中最大的三位數是多少?
*問題分析與算法設計
根據約數的定義,對於一個整數N,除去1和它自身外,凡能整除N的數即爲N的約數。因此,最簡單的方法是用2到N-1之間的所有數去除N,即可求出N的全部約數。本題只要求取約數中最大的三位數,則其取值範圍可限制在100到999之間。
*程序說明與註釋
#include<stdio.h>
void main()
{
long i;
int j;
printf(“Pleaseinput number:”);
scanf(“%ld”,&i);
for(j=999;j>=100;j–)
if(i%j==0)
{
printf(“Themax factor with 3 digits in %ld is:%d,\\n”,i,j);
break;
}
}
*運行結果
輸入:555555
輸出:The maxfactor with 3 digits in 555555 is:777
6.高次方數的尾數
求13的13次方的最後三位數
*問題分析與算法設計
解本題最直接的方法是:將13累乘13次方截取最後三位即可。
但是由於計算機所能表示的整數範圍有限,用這種“正確”的算法不可能得到正確的結果。事實上,題目僅要求最後三位的值,完全沒有必要求13的13次方的完整結果。
研究乘法的規律發現:乘積的最後三位的值只與乘數和被乘數的後三位有關,與乘數和被乘數的高位無關。利用這一規律,可以大大簡化程序。
*程序說明與註釋
#include<stdio.h>
void main()
{
inti,x,y,last=1;
printf(“InputX and Y(X**Y):”);
scanf(“%d**%d”,&x,&y);
for(i=1;i<=y;i++)
last=last*x%1000;
printf(“Thelast 3 digits of %d**%d is:%d\\n”,x,y,last%1000);
}
*運行結果
Input X andY(X**Y):13**13
The last 3 digitsof 13**13 is:253
Input X andY(X**Y):13**20
The last 3 digitsof 13**20 is:801
——————————————————————————–
8.借書方案知多少
小明有五本新書,要借給A,B,C三位小朋友,若每人每次只能借一本,則可以有多少種不同的借法?
*問題分析與算法設計
本問題實際上是一個排列問題,即求從5箇中取3個進行排列的方法的總數。首先對五本書從1至5進行編號,然後使用窮舉的方法。假設三個人分別借這五本書中的一本,當三個人所借的書的編號都不相同時,就是滿足題意的一種借閱方法。
*程序說明與註釋
void main()
{
int a,b,c,count=0;
printf(“Thereare 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 diffrentmethods 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
9.楊輝三角形
在屏幕上顯示楊輝三角形
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,inty)
{
int z;
if((y==1)||(y==x+1)) return1;
z=c(x-1,y-1)+c(x-1,y);
return z;
}
——————————————————————————–
10.數制轉換
將任一整數轉換爲二進制形式
*問題分析與算法設計
將十進制整數轉換爲二進制的方法很多,這裏介紹的實現方法利用了C語言能夠對位進行操作的特點。對於C語言來說,一個整數在計算機內就是以二進制的形式存儲的,所以沒有必要再將一個整數經過一系列的運算轉換爲二進制形式,只要將整數在內存中的二進制表示輸出即可。
*程序說明與註釋
#include<stdio.h>
void printb(int,int);
void main()
{
intx;printf(“Input number:”);
scanf(“%d”,&x);
printf(“numberof decimal form:%d\\n”,x);
printf(“ it\’sbinary 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 bunaryform:0000000000001000
輸入:-8
輸出:number of decimal form:-8
it\’s binary form:1111111111111000
輸入:32767
輸出:number of decimal form:32767
it\’s binaryform:0111111111111111
輸入:-32768
輸出:number of decimal form:-32768
it\’s binaryform:1000000000000000
輸入:128
輸出:number of decimal form:128
it\’s binaryform:0000000010000000
11.打魚還是曬網
中國有句俗語叫“三天打魚兩天曬網”。某人從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 datetoday,term;
intyearday,year,day;
printf(“Enteryear/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”);
elseprintf(“He was sleeping at that day.\\n”);
}
int days(struct date day)
{
static intday_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;
}
*運行結果
Enteryear/month/day:1991 10 25
Hewas fishing at day.
Enteryear/month/day:1992 10 25
Hewas sleeping at day.
Enteryear/month/day:1993 10 25
Hewas sleeping at day
——————————————————————————–
12.抓交通肇事犯
一輛卡車違反交通規則,撞人後逃跑。現場有三人目擊事件,但都沒有記住車號,只記下車號的一些特徵。甲說:牌照的前兩位數字是相同的;乙說:牌照的後兩位數字是相同的,但與前兩位不同;丙是數學家,他說:四位的車號剛好是一個整數的平方。請根據以上線索求出車號。
*問題分析與算法設計
按照題目的要求造出一個前兩位數相同、後兩位數相同且相互間又不同的整數,然後判斷該整數是否是另一個整數的平方。
*程序與程序註釋
#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
13.該存多少錢
假設銀行一年整存零取的月息爲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(“Hemust save %.2f at first.\\n”,total);
}
*運行結果
He must save4039.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爲對應存款年限的利率。根據題意還可得到以下限制條件:
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()
{
inti8,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(“Formaxinum profit,he should so save his money in a bank:\\n”);
printf(“ madefixed deposit for 8 year: %d times\\n”,n8);
printf(“ madefixed deposit for 5 year: %d times\\n”,n5);
printf(“ madefixed deposit for 3 year: %d times\\n”,n3);
printf(“ madefixed deposit for 2 year: %d times\\n”,n2);
printf(“ madefixed deposit for 1 year: %d times\\n”,n1);
printf(“ Toal:%.2f\\n”,max);
}
*運行結果
For maxinum profit,he should so save hismoney in a bank:
made fixed depositfor 8 year: 0times
made fixed depositfor 5 year: 4times
made fixed depositfor 3 year: 0times
made fixed depositfor 2 year: 0times
made fixed depositfor 1 year: 0times
Total:8841.01
可見最佳的存款方案爲連續四次存5年期。
*思考題
某單位對職工出售住房,每套爲2萬元。買房付款的方法是:
一次交清,優惠20%
從第一年開始,每年年初分期付款:
5年交清,優惠50%;
10年交清,優惠10%;
20年交清,沒有優惠。
現在有人手中正好有2萬元,若假定在今後20年中物價和銀行利率均保持不變,問他應當選擇哪種付款方式可以使應付的錢最少?
——————————————————————————–
15.捕魚和分魚
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()
{
intn,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;
elseflag=1;
}
printf(“Totalnumber of fish catched=%d\\n”,n);
}
*運行結果
Total number of fishcatched = 3121
*問題的進一步討論
程序採用試探法,試探的初值爲6,每次試探的步長爲1。這是過分保守的做法。可以在進一步分析題目的基礎上修改此值,增大試探的步長值,以減少試探次數。
*思考題
請使用其它的方法求解本題
16.出售金魚
買賣提將養的一缸金魚分五次出售系統上一次賣出全部的一半加二分之一條;第二次賣出餘下的三分之一加三分之一條;第三次賣出餘下的四分之一加四分之一條;第四次賣出餘下的五分之一加五分之一條;最後賣出餘下的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()
{
inti,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(“Thereare %d fishes atfirst.\\n”,i);
n=1;
}
}
}
*運行結果
There are 59 fishesat first.
*思考題
日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?
1.7 分數四則運算
對輸入的兩個分數進行+、-、*、/四則運算,輸出分數結果。
算法分析如下:
對分數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 inta,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);
}
——————————————————————————–
17.平分七筐魚
甲、乙、丙三位魚夫出海打魚,他們隨船帶了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(“Itexists 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];
elseflag=0;
if(flag)
{
printf(“No.%d Fullbasket 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 Fullbasket Semi–basket Empty
fisherA: 1 5 1
fisherB: 3 1 3
fisherC: 3 1 3
No.2 Fullbasket Semi–basket Empty
fisherA: 2 3 2
fisherB: 2 3 2
fisherC: 3 1 3
*思考題
晏會上數學家出了一道難題:假定桌子上有三瓶啤酒,癬瓶子中的酒分給幾個人喝,但喝各瓶酒的人數是不一樣的。不過其中有一個人喝了每一瓶中的酒,且加起來剛好是一瓶,請問喝這三瓶酒的各有多少人?
(答案:喝三瓶酒的人數分別是2人、3人和6人)
18.有限5位數
個位數爲6且能被3整除的五位數共有多少?
*題目分析與算法設計
根據題意可知,滿足條件的五位數的選擇範圍是10006、10016。。。99996。可設基礎數i=1000,通過計算i*10+6即可得到欲選的數(i的變化範圍是1000~999),再判斷該數能否被3整除。
*程序說明與註釋
#include<stdio.h>
void main()
{
long inti;
intcount=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)
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<stdio.h>
void main()
{
int i;
for(i=0;;i++)
if(((i*8+7)*8+1)*8+1==(34*i+15)*17+4)
{
printf(“Therequired 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(“Thespecial 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 numberwith 3 digits is:503(7)=305(9)=248(10)
——————————————————————————–
21.4位反序數
設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(“Thenumber satisfied stats condition is: %d\\n”,i);
}
*運行結果
The numbersatisfied states condition is:1089
22.求車速
一輛以固定速度行駛的汽車,司機在上午10點看到里程錶上的讀數是一個對稱數(即這個數從左向右讀和從右向左讀是完全一樣的),爲95859。兩小時後里程錶上出現了一個新的對稱數。問該車的速度是多少?新的對稱數是多少?
*題目分析與算法設計
根據題意,設所求對稱數爲i,其初值爲95589,對其依次遞增取值,將i值的每一位分解後與其對稱位置上的數進行比較,若每個對稱位置上的數皆相等,則可判定i即爲所求的對稱數。
*程序說明與註釋
#include<stdio.h>
void main()
{
intt,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(“Thenew symmetrical number kelometers is:%d%d%d%d%d\\n”,
a[0],a[1],a[2],a[3],a[4]);
printf(“Thevelocity of the car is: %.2f\\n”,(i-95859)/2.0);
break;
}
}
}
*運行結果
The new symmetricalnumber kelometers is:95959.
The velocity of thecar is:50.00
*思考題
將一個數的數碼倒過來所得到的新數叫原數的反序數。如果一個數等於它的反序數,則稱它爲對稱數。求不超過1993的最大的二進制的對稱數
23.阿姆斯特朗數
如果一個正整數等於其各個數字的立方和,則稱該數爲阿姆斯特朗數(亦稱爲自戀性數)。
如 407=43+03+73就是一個阿姆斯特朗數。試編程求1000以內的所有阿姆斯特朗數。
*題目分析與算法設計
可採用窮舉法,依次取1000以內的各數(設爲i),將i的各位數字分解後,據阿姆斯特朗數的性質進行計算和判斷。
*程序說明與註釋
#include<stdio.h>
void main()
{
int i,t,k,a[3];
printf(“Thereare 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 followingArmstrong number smaller than 1000:
153 370 371 407
——————————————————————————–
24.完全數
如果一個數恰好等於它的因子之和,則稱該數爲“完全數”。
*題目分析與算法設計
根據完全數的定義,先計算所選取的整數a(a的取值1~1000)的因子,將各因子累加於m,若m等於a,則可確認a爲完全數。
*程序說明與註釋
#include<stdio.h>
void main()
{
int a,i,m;
printf(“Thereare 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 arefollowing perfect numbers smaller than 1000:
6 28 496
26.親密數
如果整數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(“Thereare 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 followingfriendly–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 積
本問題所關心的是積的最後三位。分析產生積的後三位的過程,可以看出,在每一次的部分積中,並不是它的每一位都會對積的後三位產生影響。總結規律可以得到:在三位數乘法中,對積的後三位產生影響的部分積分別爲:
第一個部分積中:被乘數最後三位*乘數的倒數第一位
第二個部分積中:被乘數最後二位*乘數的倒數第二位
第三個部分積中:被乘數最後一位*乘數的倒數第三位
將以上的部分積的後三位求和後截取後三位就是三位數乘積的後三位。這樣的規律可以推廣到同樣問題的不同位數乘積。
按照手工計算的過程可以設計算法編寫程序。
*程序說明與註釋
#include<stdio.h>
void main()
{
longmul,number,k,ll,kk;
printf(“Itexists 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 followingautomorphic 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爲回亠數。
*程序說明與註釋
#include<stdio.h>
void main()
{
intm[16],n,i,t,count=0;
long unsigned a,k;
printf(“No. number it\’ssquare(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\’ssquare(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,然後套用公式計算並判斷。
*程序說明與註釋
#include<stdio.h>
void main()
{
int n,a,b;
printf(“Thereare 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 followingnumbers with 4 digits satisfied condition:
2025 3025 9801
30.求素數
求素數表中1~1000之間的所有素數
*問題分析與算法設計
素數就是僅能衩1和它自身整除的整數。判定一個整數n是否爲素數就是要判定整數n能否被除1和它自身之外的任意整數整除,若都不能整除,則n爲素數。
程序設計時i可以從2開始,到該整數n的1/2爲止,用i依次去除需要判定的整數,只要存在可以整除該數的情況,即可確定要判斷的整數不是素數,否則是素數。
*程序與程序註釋
#include<stdio.h>
void main()
{
intn1,nm,i,j,flag,count=0;
do{
printf(“InputSTART and END=?”);
scanf(“%d%d”,&n1,&nm);
}while(!(n1>0&&n1<nm));
printf(“………..PRIMETABLE(%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);
}
}
31.歌德巴赫猜想
驗證: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(inti)
{
int j;
if(i<=1)return0;
if(i==2)return 1;
if(!(i%2))return0;
for(j=3;j<=(int)(sqrt((double)i)+1);j+=2)
if(!(i%j))return0;
return1;
}
——————————————————————————–
32.要發就發
“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
intnumber[NUM];
int fflag(int i);
void main()
{
int i,j,count=0;
printf(“thereare 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) return0;
if(i==2) return 1;
if(!(i%2)) return0;
for(j=3;j<=(int)(sqrt((double)i)+1);j+=2)
if(!(i%j))return 0;
return 1;
}
*運行結果
There are follwingprimes sequences in first row:
(1).89,……,1987
(2).53,……,1951
(3). 3,……,1901
35.素數幻方
求四階的素數幻方。即在一個4X4的矩陣中,每一個格填入一個數字,使每一行、每一列和兩條對角線上的4 個數字所組成的四位數,均爲可逆素數。
*問題分析與算法設計
有了前面的基礎,本題應當說是不困難的。
最簡單的算法是:採用窮舉法,設定4X4矩陣中每一個元素的值後,判斷每一行、每一列和兩條對角線上的4個數字組成的四位數是否都是可逆素數,若是則求出了滿足題意的一個解。
這種算法在原理是對的,也一定可以求出滿足題意的全部解。但是,按照這一思路編出的程序效率很低,在微機上幾個小時也不會運行結束。這一算法致命的缺陷是:要窮舉和判斷的情況過多。
充分利用題目中的“每一個四位數都是可逆素數”這一條件,可以放棄對矩陣中每個元素進行的窮舉的算法,先求出全部的四位可逆素數(204個),以矩陣的行爲單位,在四位可逆素數的範圍內進行窮舉,然後將窮舉的四位整數分解爲數字後,再進行列和對角線方向的條件判斷,改進的算法與最初的算法相比,大大地減少了窮舉的次數。
考慮矩陣的第一行和最後一行數字,它們分別是列方向四位數的第一個數字和最後一個數字,由於這些四位數也必須是可逆素數,所以矩陣的每一行和最後一行中的各個數字都不能爲偶數或5。這樣窮舉矩陣的第一行和最後一行時,它們的取值範圍是:所有位的數字均不是偶數或5的四位可逆數。由於符合這一條件的四位可逆素數很少,所以這一範圍限制又一次減少了窮舉的次數。
對算法的進一步研究會發現:當設定了第一和第二行的值後,就已經可以判斷出當前的這種組合是否一定是錯誤的(尚不能肯定該組合一定是正確的)。若按列方向上的四個兩位數與四位可逆數的前兩位矛盾(不是其中的一種組合),則第一、二行的取值一定是錯誤的。同理在設定了前三行數據後,可以立刻判斷出當前的這種組合是否一定是錯誤的,若判斷出矛盾情況,則可以立刻設置新的一組數據。這樣就可以避免將四個數據全部設定好以後再進行判斷所造成的低效。
根據以上分析,可以用僞語言描述以上改進的算法:
開始
找出全部四位的可逆素數;
確定全部出現在第一和最後一行的四位可逆素數;
在指定範圍 內窮舉第一行
在指定範圍內窮舉第二行
若第一、第二、三行已出現矛盾,則繼續窮舉下一個數;
在指定範圍內窮舉第四行
判斷列和對角方向是否符合題意
若符合題意,則輸出矩陣;
否則繼續窮舉下一個數;
結束
在實際編程中,採用了很多程序設計技巧,假如設置若干輔助數組,其目的就是要最大限度的提高程序的執行效率,縮短運行時間。下面的程序運行效率是比較高的。
*程序與程序註釋
#include<stdio.h>
#include<math.h>
int number[210][5];
int select[110];
intarray[4][5];
intcount;
intselecount;
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()
{
inti,k,flag,cc=0,i1,i4;
printf(“thereare 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(intnumber)
{
int j;
if(!ok(number))return 0;
for(j=0;number>0;number/=10)
j=j*10+number%10;
if(!ok(j)) return0;
return 1;
}
int ok(intnumber)
{
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(inti)
{
int j,num;
num=number[0];
for(j=4;j>=1;j–,num/=10)
number[j]=num%10;
}
void copy_num(inti)
{
int j;
for(j=1;j<=4;j++)
array[j]=number[array[0>[j];
}
int comp_num(intn)
{
static intii;
static intjj;
inti,num,k,*p;
int*pcount;
switch(n){
case2:pcount=&lcount[0];p=ⅈbreak;
case3:pcount=&lcount[1];p=&jj;break;
default:return0;
}
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])
return0;
}
return 1;
}
int find1(inti)
{
int num,j;
for(num=0,j=0;j<4;j++)
num=num*10+array[j];
return find0(num);
}
intfind2(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(intnum)
{
static int j;
if(num<=number[j][0])for(;j>=0&&num<number[j][0];j–);
elsefor(;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;
}
voidp_array(void)
{
int i,j;
for(i=0;i<4;i++)
{
for(j=1;j<=4;j++)printf(“%d “,array[j]);
printf(“\\n”);
}
}
——————————————————————————–
36.百錢百雞問題
中國古代數學家張丘建在他的《算經》中提出了著名的“百錢買百雞問題”:雞翁一,值錢五,雞母一,值錢三,雞雛三,值錢一,百錢買百雞,問翁、母、雛各幾何?
*題目分析與算法設計
設雞翁、雞母、雞雛的個數分別爲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(“Folleingare 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=%2dhen=%2d chicken=%2d\\n”,++j,x,y,z);
}
}
*運行結果
Follwing are possible plans to buy 100fowls with 100 Yuan.
1:cock=0 hen=25chicken=75
2:cock=4 hen=18chicken=78
3:cock=8 hen=11chicken=81
4:cock=12 hen=4chicken=84
*總是的進一步討論
這類求解不定方程總理的實現,各層循環的控制變量直接與方程未知數有關,且採用對未知數的取值範上窮舉和組合的方法來複蓋可能得到的全部各組解。能否根據題意更合理的設置循環控制條件來減少這種窮舉和組合的次數,提高程序的執行效率,請讀者考慮。
37.愛因斯坦的數學題
愛因斯坦出了一道這樣的數學題:有一條長階梯,若每步跨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()
{
inti=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值。請自行編寫程序實現。
39.年齡幾何
張三、李四、王五、劉六的年齡成一等差數列,他們四人的年齡相加是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(“Theseries 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 withequal difference are:
2 5 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53 56 59
38.換分幣
用一元人民幣兌換成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(“Thereare 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);
}
40.三色球問題
若一個口袋中放有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(“ REDBALL WHITEBALL 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);
}
41.馬克思手稿中的數學題
馬克思手稿中有一道趣味數學問題:有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);
}
}
42.最大公約數和最小公倍數
求任意兩個正整數的最大公約數和(GCD)和最小公倍數(LCM)
*問題分析與算法設計
手工方式求兩個正整數的蝚大公約數的方法是用輾轉相除法,在程序中可以模擬這種方式。
*程序與程序註釋
#include<stdio.h>
void main()
{
inta,b,num1,num2,temp;
printf(“Inputa & 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(“TheGCD of %d and %d is: %d\\n”,num1,num2,a);
printf(“TheLCM of them is: %d\\n”,num1*num2/a);
}
*運行結果
1.Input a & b:20 55
TheGCD of 20 and 55 is: 5
TheLCM of them is: 220
2.Input a & b:17 71
TheGCD of 17 and 71 is: 1
TheLCM of them is: 1207
3.Input a & b:24 88
TheGCD of 24 and 88 is: 8
TheLCM of them is: 264
4.Input a & b:35 85
TheGCD of 35 and 85 is: 5
TheLCM of them is: 595
43.分數比較
比較兩個分數的大小。
*問題分析與算法設計
人工方式下比較分數大小最常用的方法是:進行分數的通分後比較分子的大小。可以編程模擬手式方式。
*程序與程序註釋
#include<stdio.h>
int zxgb(int a,int b);
void main()
{
int i,j,k,l,m,n;
printf(“Inputtwo 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
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<r<13
採用最簡單的窮舉方法可以很方便的求解。
程序與程序註釋:
#include<stdio.h>
void main()
{
intp,q,r,s,count=0;
printf(“The 4fractions 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);
}
}
——————————————————————————–
45.將真分數分解爲埃及分數
分子爲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(“Pleaseenter a optional fraction(a/b):”);
scanf(“%ld/%ld”,&a,&b);
printf(“It canbe 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 aoptional fraction (a/b): 1/6
Itcan be decomposed t 1/6
2. Please enter aoptional fraction (a/b): 20/33
Itcan be decomposed t 1/2+1/10+1/165
3. Please enter aoptional fraction (a/b): 10/89
Itcan be decomposed t 1/9+1/801
4. Please enter aoptional fraction (a/b): 19/99
Itcan be decomposed t 1/6+1/40+1/3960
5. Please enter aoptional fraction (a/b): 8/89
Itcan be decomposed t 1/11+1/957
46.列出真分數序列
按遞增順序依次列出所有分母爲40,分子小於40的最簡分數。
*問題分析與算法設計
對分子採用窮舉法,利用最大公約數的方法,判斷分子與40是否構成真分數。
*程序與程序註釋
#include<stdio.h>
void main()
{
inti,num1,num2,temp;
printf(“Thefraction 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 fractionserials 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的最簡真分數
——————————————————————————–
47.計算分數的精確值
使用數組精確計算M/N(0<M<N<=100)的值。如果M/N是無限循環小數,則計算並輸出它的第一循環節,同時要求輸出 循環節的起止位置(小數位的序號)
*問題分析與算法設計
由於計算機字長的限制,常規的浮點運算都有精度限制,爲了得到高精度的計算結果,就必須自行設計實現方法。
爲了實現高精度的計算,可將商存放在一維數組中,數組的每個元素存放一位十進制數,即商的第一位存放在第一個元素中,商的第二位存放在第二個元素中….,依次類推。這樣就可以使用數組不表示一個高精度的計算結果。
進行除法運算時可以模擬人的手工操作,即每次求出商的第一位後,將餘數乘以10,再計算商的下一位,重複以上過程,當某次計算後的餘數爲0 時,表示M/N爲有限不循環小數某次計算後的餘數與前面的某個餘數相同時,則M/N爲無限循環小數,從該餘數第一次出現之後所求得的各位數就是小數的循環節。
程序具體實現時,採用了數組和其它一些技巧來保存除法運算所得到的餘數和商的各位數。
*程序與程序註釋
#include<stdio.h>
intremainder[101],quotient[101];
void main()
{
int m,n,i,j;
printf(“Pleaseinput a fraction(m/n)(<0<m<n<=100):”);
scanf(“%d/%d”,&m,&n);
printf(“%d/%dit\’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\\tandit is a infinite cyclic fraction from %d\\n”,remainder[m]);
printf(“\\tdigitto %d digit after decimal point.\\n”,i);
break;
}
}
}
——————————————————————————–
51.誰是竊賊
公安人員審問四名竊賊嫌疑犯。已知,這四人當中僅有一名是竊賊,還知道這四人中每人要麼是誠實的,要麼總是說謊的。在回答公安人員的問題中:
甲說:“乙沒有偷,是丁偷的。”
乙說:“我沒有偷,是丙便的。”
丙說:“甲沒有偷,是乙偷的。”
丁說:“我沒有偷。”
請根據這四人的答話判斷誰是盜竊者。
*問題分析與算法設計
假設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;
elsea[j]=0;
if(a[3]+a[1]==1&&a[1]+a[2]==1&&a[0]+a[1]==1)
{
printf(“Thethiefis ”);
for(j=0;j<=3;j++)
if(a[j])printf(“%c.”,j+\’A\’);
printf(“\\n”);
}
}
}
*運行結果
The thief isB. (乙爲竊賤。)
—————————————————-
52.黑與白
有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(“Ais pasted a piece of %s paper on his forehead.\\n”,
a?”white”:”black”);
printf(“Bis pasted a piece of %s paper on his forehead.\\n”,
b?”white”:”black”);
printf(“Cis pasted a piece of %s paper on his forehead.\\n”,
c?”white”:”black”);
printf(“Dis pasted a piece of %s paper on his forehead.\\n”,
d?”white”:”black”);
printf(“Eis pasted a piece of %s paper on his forehead.\\n”,
e?”white”:”black”);
}
}
*運行結果
A is pasted a paperof black paper on his forehead. (黑)
B is pasted a paperof black paper on hisforehead. (黑)
C is pasted a paperof white paper on hisforehead. (白)
D is pasted a paperof black paper on hisforehead. (黑)
E is pasted a paperof white paper on hisforehead (白)
——————————————————————————–
53.迷語博士的難題(1)
誠實族和說謊族是來自兩個荒島的不同民族,誠實族的人永遠說真話,而說謊族的人永遠說假話。迷語博士是個聰明的人,他要來判斷所遇到的人是來自哪個民族的。
迷語博士遇到三個人,知道他們可能是來自誠實族或說謊族的。爲了調查這三個人是什麼族的,博士分別問了他們的問題,這是他們的對話:
問第一個人:“你們是什麼族?”,答:“我們之中有兩個來自誠實族。”第二個人說:“不要胡說,我們三個人中只有一個是誠實族的。”第三個人聽了第二個人的話後說:“對,就是隻有一個誠實族的。”
請根據他的回答判斷他們分別是哪個族的。
*問題分析與算法設計
假設這三個人分別爲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(“Ais a %s.\\n”,a?”honest”:”lier”);
printf(“Bis a %s.\\n”,b?”honest”:”lier”);
printf(“Cis a %s.\\n”,c?”honest”:”lier”);
}
}
*運行結果
A is alier (說謊族)
B is alier (說謊族)
C is alier (說謊族)
*思考題
迷語博士遇到四個人,知道他們可能是來自誠實族和說謊族的。爲了調查這四個人是什麼族的,博士照例進行詢問:”你們是什麼族的?“
第一人說:”我們四人全都是說謊族的。“
第二人說:”我們之中只有一人是說謊族的。“
第三人說:”我們四人中有兩個是說謊族的。“
第四人說:”我是誠實族的。“
問自稱是“誠實族”的第四個人是否真是誠實族的?
(答案:第四個人是誠實族的。)
———————————————————-
54.迷語博士的難題(2)
兩面族是荒島上的一個新民族,他們的特點是說話真一句假一句且真假交替。如果第一句爲真,則第二句是假的;如果第一句爲假的,則第二句就是真的,但是第一句是真是假沒有規律。
迷語博士遇到三個人,知道他們分別來自三個不同的民族:誠實族、說謊族和兩面族。三人並肩站在博士前面。
博士問左邊的人:“中間的人是什麼族的?”,左邊的人回答:“誠實族的”。
博士問中間的人:“你是什麼族的?”,中間的人回答:“兩面族的”。
博士問右邊的人:“中間的人究竟是什麼族的?”,右邊的人回答:“說謊族的”。
請問:這三個人都是哪個民族的?
*問題分析與算法設計
這個問題是兩面族問題中最基本的問題,它比前面只有誠實族和說謊族的問題要複雜。解題時要使用變量將這三個民族分別表示出來。
令:變量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(“Theman stand on left is a %s.\\n”,
aa?”double–dealer”:(a?”honest”:”lier”));
printf(“Theman stand on left is a %s.\\n”,
bb?”double–dealer”:(b?”honest”:”lier”));
printf(“Theman stand on left is a %s.\\n”,
cc?”double–dealer”:(c?”honest”:”lier”));
}
}
*運行結果
The man stand onleft is a double–dealer. (左邊的人是兩面族的)
The man stand oncenter is alier. (中間的人是說謊族的)
The man stand onright is ahonest. (右邊的人是誠實族的)
*思考題
迷語博士遇到三個人,便問第一個人:“你是什麼族的?”,回答:“誠實族的。”問第二個人:“你是什麼族的?”,答:“說謊族的。”博士又問第二個人:“第一個人真的是誠實族的嗎?”,答:“是的。”問第三個人:“你是什麼族的?”,答:“誠實族的。”博士又問第三個人:“第一個人是什麼族的?”,答:“兩面族的。”
請判斷這個人到底是哪個民族的?
(答案:第一個人是誠實族的,第二個人是兩面族的,第三人是說謊族。)
55.哪個大夫哪天值班
醫院有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;
elsecontinue;
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;
elsecontinue;
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 dutyMONDAY. (星期一:E)
Doctor B is on dutyTUESDAY. (星期二:B)
Doctor D is on dutyWEDNESDAY. (星期三:D)
Doctor F is on dutyTHUESDAY. (星期四:F)
Doctor G is on dutyFRIDAY. (星期五:G)
Doctor C is on dutySATURDAY. (星期六:C)
Doctor A is on dutySUNDAY. (星期日:A)
*思考題
在本題的求解過程中,我們只考慮了一星期之內的情況,沒有考慮跨周的情況。對於“B大夫比G大夫早三天值班的”條件只是簡單的認爲是在同一周內早三天。若考慮跨周的情況就可能出現:B大夫星期一值班,而G大夫是上週的星期五。同樣,對“F大夫的值班日在B和C大夫的中間”這個條件,也可以擴展爲:“只要F大夫的值班日在B和C大夫的中間就可以”。
請考慮允許跨周的情況下,可能的時間安排表。
——————————————————-
56.區分旅客國籍
在一個旅館中住着六個不同國籍的人,他們分別來自美國、德國、英國、法國、俄羅斯和意大利。他們的名字叫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()
{
inta[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(“%cis 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 fromITALY. (意大利人)
B is coming fromEUSSIAN. (俄羅斯人)
C is coming fromU.K.. (英國人)
D is coming fromGER. (德國人)
E is coming fromFRANCE. (法國人)
F is coming fromU.S.. (美國人)
*問題的進一步討論
生成條件矩陣然後使用消去法進行推理判斷是一種常用的方法。對於解決較爲複雜的邏輯問題是十分有效的。
*思考題
地理課上老師給出一張沒有說明省份的中國地圖,從中選出五個省從1到5編號,要大家寫出省份的名稱。交卷後五位同學每人只答了二個省份的名稱如下,且每人只答對了一個省,問正確答案是什麼?
A答:2號陝西,5號甘肅 B 答:2號湖北,4號山東
C答:1號山東,5號吉林 D 答:3號湖北,4號吉林
E答:2號甘肅,3號陝西
57.誰家孩子跑最慢
張王李三家各有三個小孩。一天,三家的九個孩子在一起比賽短跑,規定不分年齡大小,跑第一得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(“Thelast one arrived to end is a child from family Zhang.\\n”);
elseif(who==2)
printf(“Thelast one arrived to end is a child from family Wang.\\n”);
elseprintf(“The last one arrived to end is a child from family Li.\\n”);
}
*運行結果
7 5 3
8 6 1
9 4 2
The last onearrived 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<stdio.h>
#define N6
void main()
{
int i,j,k,t;
printf(“Thepossble 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 LatinSquares of order 6 are:
1 2 3 4 56 2 3 45 6 1 34 5 6 1 2
2 3 4 5 61 3 4 56 1 2 45 6 1 2 3
3 4 5 6 12 4 5 61 2 3 56 1 2 3 4
4 5 6 1 23 5 6 12 3 4 61 2 3 4 5
5 6 1 2 34 6 1 23 4 5 12 3 4 5 6
6 1 2 3 45 1 2 34 5 6 23 4 5 6 1
4 5 6 1 23 5 6 12 3 4 61 2 3 4 5
5 6 1 2 34 6 1 23 4 5 12 3 4 5 6
6 1 2 3 45 1 2 34 5 6 23 4 5 6 1
1 2 3 4 56 2 3 45 6 1 34 5 6 1 2
2 3 4 5 61 3 4 56 1 2 45 6 1 2 3
3 4 5 6 12 4 5 61 2 3 56 1 2 3 4
—————————————————————-
59.填表格
將1、2、3、4、5和6 填入下表中,要使得每一列右邊的數字比左邊的數字大,每一行下面的數字比上面的數字大。按此要求,可有幾種填寫方法?
. . .
. . .
*問題分析與算法設計
按題目的要求進行分析,數字1一定是放在第一行第一列的格中,數字6一定是放在第二行第三列的格中。在實現時可用一個一維數組表示,前三個元素表示第一行,後三個元素表示第二行。先根據原題初始化數組,再根據題目中填寫數字的要求進行試探。
*程序與程序註釋
#include<stdio.h>
int jud1(int s[]);
void print(int u[]);
intcount;
void main()
{
static inta[]={1,2,3,4,5,6};
printf(“Thepossble 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) return0;
return1;
}
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 tablesatisfied above conditions are:
No.1: No.2: No.3: No.4: No.5:
1 23 1 24 1 25 1 34 1 3 5
4 56 3 56 3 46 2 56 2 4 6
——————————————————–
60.1~9分成1:2:3的三個3位數
將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)return0;
}
return1;
}
*運行結果
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種可能的組合)
——————————————————————————–
61.1~9組成三個3位的平方數
將1、2、3、4、5、6、7、8、9九個數字分成三組,每個數字只能用一次,即每組三個數不允許有重複數字,也不許同其它組的三個數字重複,要求每組中的三位數都組成一個平方數。
*問題分析與算法設計
本問題的思路很多,這裏介紹一種簡單快速的算法。
首先求出三位數中不包含0且是某個整數平方的三位數,這樣的三位數是不多的。然後將滿足條件的三位數進行組合,使得所選出的3個三位數的9個數字沒有重複。
程序中可以將尋找足條件的三位數的過程和對該三位數進行數字分解的過程結合起來。
*程序與程序註釋
#include<stdio.h>
void main()
{
inta[20],num[20][3],b[10];
inti,j,k,m,n,t,flag;
printf(“The 3squares 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 with3 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<stdio.h>
#include<stdlib.h>
void main()
{
inta[9],ii=0,i,a1,a2,a3,a4,b1,b2,b3,b4,flag;
for(i=1;i<=8;i++)
{
printf(“Pleaseenter [%d]number:”,i);
scanf(“%d”,&a);
ii+=a;
}
printf(“******************************************\\n”);
if(ii%2)
{
printf(“Sorrythey 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;gotoout;
}
out:
if(flag)
{
printf(“Theycan 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(“Sorrythey can\’t be constructed required cube!\\n”);
——————————————————————————–
63.減式還原
編寫程序求解下式中各字母所代表的數字,不同的字母代表不同的數字。
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
———————————————————–
64.乘式還原
A代表數字0到9中的前五個數字,Z代表後五個數字,請還原下列乘式。
AZ A
× AA Z
————
AA A A
AA Z Z
ZA A
————
ZA Z A A
*問題分析與算法設計
問題本身並不複雜,可以對乘式中的每一位使用窮舉法,最終可以得到結果。本題的關鍵在於怎樣有效的判斷每個部分積的每一位是否滿足題意,這一問題處理不好,編寫的程序會很長。程序實現中採用了一個判斷函數,通過傳入函數的標誌字符串對所有的數進行統一的判斷處理。
*程序與程序註釋
#include<stdio.h>
void print(long a,long b,long s1,longs2,long s3);
int jud(long q,char *pflag);
void main()
{
longi,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,longs2,longs3)
{
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))
return0;
else
{
q/=10;++pflag;
}
if(q==0&&*pflag==NULL)
return1;
else return 0;
}
*運行結果
37 2
×2 4 6
———-
22 3 2
14 8 8
74 4
————
91 5 1 2
*思考題
E代表數字0到9中的偶數數字,O代表奇數數字,請還原下列乘式。
EEO 28 5
× OO 答案 × 3 9
———– ———–
EO EO 25 6 5
EOO 85 5
———– ———–
OO O OO 11 1 1 5
65.乘式還原(2)
有乘法算式如下:
○○○
× ○○
————
○○○○
○○○○
————
○○○○○
18個○的位置上全部是素數(1、3、5或7),請還原此算式。
*問題分析與算法設計
問題中雖然有18數位,但只要確定乘數和被乘數後經過計算就可確定其它的數位。
乘數和被乘數共有5個數位,要求每個數都是質數。完全可以採用窮舉的方法對乘數和被乘數進行窮舉,經過判斷後找出答案。但是這種方法給人的感覺是“太笨了”,因爲組成的數字只是質數(4個),完全沒有必要在那麼大的範圍內進行窮舉,只需要試探每一位數字爲質數時的情況即可。
採用五重循環的方法實現對於5個數字的窮舉,前面的許多例題中都已見過。循環實現簡單易行,但嵌套的層次太多,需要窮舉的變量的數量直接影響到循環嵌套的層數,這種簡單的實現方法缺少技巧性。本例的程序中給出了另外一種同樣功能的算法,該算法的實現思想請閱讀程序。
程序中並沒有直接對質數進行窮舉,而是將每個質數與1到4順序一一對應,在窮舉時爲處理簡單僅對1到4進行窮舉處理,待要判斷產生的乘積是否滿足條件時再利用一個數組完成向對應質數的轉換。請體會程序中的處理方法。程序中使用的算法實際上是回朔法。
*程序與程序註釋
#include<stdio.h>
#define NUM5
#define C_NUM4
inta[NUM+1];
intb[]={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;
elsea[i–]=0;
elsea[i++]++;
if(not_finish)
{
longint 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(longsum)
{
inti,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;
elsesum=sum/10;
}
return 1;
}
*運行結果
77 5
× 33
———-
23 2 5
2 3 2 5
———–
2 5 5 7 5
*思考題
以下乘式中,A、B、C代表一確定的數字,○代表任意數字,請復原。
ABC 28 6
× BAC × 82 6
————- 答案: ————
○○○○ 17 1 6
○○A 57 2
○○○B 22 8 8
————- —————-
○○○○○○ 23 6 2 3 6
——————————————————————————–
66.除式還原(1)
給定下列除式,其中包含5個7,其它打×的是任意數字,請加以還原。
×7 × ————–商
————–
除數——××| ×××××————-被除數
×77
————–
×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。
可以看作爲下列算式:
97 1
————-
53| 5 1 4 6 3
47 7
————-
37 6
37 1
———–
53
53
———–
○
*問題的進一步討論
在推出的已知條件中,幾所有的條件都是十分明顯的,換句話說,推出的已知條件就是對題目的平鋪直敘。這種推已知條件的方法十分簡單,並且行之有效。
*思考題
下列除式中僅給定了一個8,其它打×的位置上是任意數字,請還原。
×8 × —————-商
—————-
除數——-×××| ××××××—————被除數
××××
—————
×××
×××
—————
××××
××××
—————
○
——————————————————–
67.除式還原(2)
下列除式中僅在商中給定了一個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
——————————————————————————–
68.九位累進可除數
求九位累進可除數。所謂九位累進可除數就是這樣一個數:這個數用到1到9這九個數字組成,每個數字剛好只出現一次。這九個位數的前兩位能被2整除,前三位能被3整除……前N位能被N整除,整個九位數能被9整除。
*問題分析與算法設計
問題本身可以簡化爲一個窮舉問題:只要窮舉每位數字的各種可能取值,按照題目的要求對窮舉的結果進行判斷就一定可以得到正確的結果。
問題中給出了“累進可除”這一條件,就使得我們可以在窮舉法中加入條件判斷。在窮舉的過程中,當確定部分位的值後,馬上就判斷產生的該部分是否符合“累進可除”條件,若符合,則繼續窮舉下一位數字;否則剛剛產生的那一位數字就是錯誤的。這樣將條件判斷引入到窮舉法之中,可以儘可能早的發現矛盾,儘早地放棄不必要窮舉的值,從而提高程序的執行效率。
爲了達到早期發現矛盾的目的,不能採用多重循環的方法實行窮舉,那樣編出的程序質量較差。程序中使用的算法不再是窮舉法,而是回朔法。
*程序與程序註釋
#include<stdio.h>
#define NUM 9
int a[NUM+1];
void main()
{
inti,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;
elseif(i==1&&a==NUM)
not_finish=0;
elsea++;
}
elseif(a==NUM) a=1;
elsea++;
}
else
if(++i<=NUM)
if(a[i-1]==NUM)a=1;
elsea=a[i-1]+1;
}
if(not_finish)
{
printf(“\\nTheprogressire divisiable number is:”);
for(k=1;k<=NUM;k++)
printf(“%d”,a[k]);
if(a[NUM-1]<NUM)a[NUM-1]++;
elsea[NUM-1]=1;
not_finish=0;
printf(“\\n”);
}
}
}
*運行結果
Theprogressire 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…,直到放入全部3張牌。注意在計數時要跳過非空的盒子,只對空盒子計數。最後牌在盒子中的順序,就是魔術師手中原來牌的順序。
這種人工的方法是行之有效的,計算機可以模擬求解。
*程序與程序註釋
#include<stdio.h>
int a[14];
void main()
{
inti,n,j=1;
printf(“Theoriginal 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”);
}
*運行結果
Theoriginal order of cardsis:1 8 2 5 10 3 12 11 9 4 7 6 13
—————————————————————–
70.魔術師的猜牌術(2)
魔術師再次表演,他將紅桃和黑桃全部迭在一起,牌面朝下放在手中,對觀衆說:最上面一張是黑桃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(“Theoriginal 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”);
}
*運行結果
Theoriginal 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 r11b7 r5 r1 b8 r8 r10 b9 r2
71.約瑟夫問題
這是17世紀的法國數學家加斯帕在《數目的遊戲問題》中講的一個故事:15個教徒和15 個非教徒在深海上遇險,必須將一半的人投入海中,其餘的人才能倖免於難,於是想了一個辦法:30個人圍成一圓圈,從第一個人開始依次報數,每數到第九個人就將他扔入大海,如此循環進行直到僅餘15個人爲止。問怎樣排法,才能使每次投入大海的都是非教徒。
*問題分析與算法設計
約瑟夫問題並不難,但求解的方法很多;題目的變化形式也很多。這裏給出一種實現方法。
題目中30個人圍成一圈,因而啓發我們用一個循環的鏈來表示。可以使用結構數組來構成一個循環鏈。結構中有兩個成員,其一爲指向下一個人的指針,以構成環形的鏈;其二爲該人是否被扔下海的標記,爲1表示還在船上。從第一個人開始對還未扔下海的人進行計數,每數到9時,將結構中的標記改爲0,表示該人已被扔下海了。這樣循環計數直到有15個人被扔下海爲止。
*程序與程序註釋
#include<stdio.h>
struct node
{
intnextp;
intno_out;
}link[31];
void main()
{
int i,j,k;
printf(“Theoriginal 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;
}
elsebreak;
link[j].no_out=0;
}
for(i=1;i<=30;i++)
printf(“%c”,link.no_out?\’@\’:\’+\’);
printf(“\\n”);
}
*運行結果
The original circleis(+:pagandom, @:christian):
+++@@+@+@@@+@+++@@+@@@+++@+@@+
(+”表示被扔下海海的非教徒 @:留在船上活命的教徒)
*思考題
有N個小孩圍 成一圈並依次編號,教師指定從第M個小孩開始報數,報到第S個小孩即令其出列。然後從下一個孩子繼續報數,數到第S個小孩又令其出列,如此直到所有的孩子都出列。求小孩出列的先後順序。
——————————————————————————–
72.郵票組合
某人有四張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(“%dkinds:”,n);
for(k=0;a[k];k++)
printf(“%d”,a[k]);
printf(“\\n”);
}
*運行結果
19kinds:5 10 15 3 8 13 18 6 11 16 21 9 14 19 24 12 17 22 27
————————————————————-
73 和數能表示1~23的5個正整數
已知五個互不相同的正整數之和爲23,且從這五個數中挑選若干個加起來可以表示從1到23之內的全部自然數。問這五個數是什麼?
*問題分析與算法設計
從計算機程序設計的角度來說,可以用窮舉法分解23,然後判斷所分解的五個數是否可以表示1到23 之間的全部整數。
*程序與程序註釋
#include<stdio.h>
void main()
{
inta,b,c,d,e,i,j,k,l,m,x,count=0,f=0;
printf(“There arefollowing 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);
}
}
*運行結果
Thereare following possble result:
[1]: 12 3 5 12
[2]: 12 3 6 11
[3]: 12 3 7 10
[4]: 12 4 5 11
[5]: 12 4 6 10
[6]: 12 4 7 9
74.可稱1~40磅的4塊砝碼
法國數學家梅齊亞克在他著名的《數字組合遊戲》(1962)中提出了一個問題:一位商人有一個重40磅的砝碼,一天不小心將砝碼摔成了四塊。後來商人稱得每塊的重量都是整磅數,而且發現這四塊碎片可以在天平上稱1至40磅之間的任意重量。請問這四塊碎片各重多少?
*問題分析與算法設計
本題是上一題的發展。題目中給出的條件是“在天平上”,這意味着:同一砝碼既可以放在天平的左側,也可以放在天平的右側。若規定重物只能放在天平的左側,則當天平平衡時有:
重物重量+左側砝碼重量總和=右側砝碼重量總和
由此可得:
重物重量=右側砝碼重量總和-左側砝碼重量總和
編程時只要根據以上公式,使“右側砝碼重量總和-左側砝碼重量總和”可以表示1到40之間的全部重量即可。編程中要注意的是:怎樣採用一種簡單的方法來表示一個砝碼是在天平的左側還是在天平的右側,或是根本沒有使用。
以下程序採用1、 -1和0分別表示上述三種情況,請注意理解。
*程序與程序註釋
#include<stdio.h>
#include<math.h>
void main()
{
intweight1,weight2,weight3,weight4,d1,d2,d3,d4,x,flag;
printf(“The weightis 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;
}
}
*運行結果
Theweight is broke up as following 4 pieces:1 3 9 27
———————————————————-
75.10個小孩分糖果
十個小孩圍成一圈分糖果,老師分給第一個小孩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 intsweet[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(ints[])
{
int k;
printf(“ %2d”,j++);
for(k=0;k<10;k++) printf(“%4d”,s[k]);
printf(“\\n”);
}
——————————————————————————–
76.小明買書
小明假期同爸爸一起去書店,他選中了六本書,每本書的單價分別爲: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 enterthe 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;
}
elseif(min-x>1.e-6)
{
min=x;b[0]=flag; i=1;
}
elseif(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]);
elseprintf(“%.2f\\n”,c[j]);
}
}
*運行結果
Pleaseenter the prices of 6books: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
*思考題
可以看出,程序中求六個數所能產生全部組合的算法並不好,使用六重循環進行處理使程序顯得不夠簡潔。可以設計出更通用、優化的算法產生全部組合。
77.波鬆瓦酒的分酒趣題
法國著名數學家波瓦松在表年時代研究過一個有趣的數學問題:某人有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);
inti;
void main()
{
int a,y,z;
printf(“input Fulla,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)
{
intb=0,c=0;
printf(“ a%db%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;}
elseif(c==z)
{ a+=z;c=0;}
elseif(b>z-c)
{b-=(z-c);c=z;}
else{c+=b; b=0;}
printf(“%4d %4d %4d\\n”,a,b,c);
}
}
78.求π的近似值
請利用“正多邊形逼近”的方法求出π的近似值
*問題分析與算法設計
利用“正多邊形逼近”的方法求出π值在很早以前就存在,我們的先人祖沖之就是用這種方法在世界上第一個得到精確度達小數點後第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 inti;
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 numberof edges of required polygon:%ld\\n”,i);
}
*運行結果
pai=3.141592653589794
Thenumber of edges of required polygon:100663296
*思考題
請用外切正多邊形逼近的方法求π的近似值。
79.求π的近似值(2)
利用隨機數法求π的近似值
*問題分析與算法設計
隨機數法求π的近似值的思路:在一個單位邊長的正方形中,以邊長爲半徑,以一個頂點爲圓心,在政權方形上作四分之一圓。隨機的向正方形內扔點,若落入四分之一圓內則計數。重複向正方形內扔足夠多的點,將落入四分之一圓內的計數除以總的點數,其值就是π值四分之一的近似值。
按此方法可直接進行編程,注意:本方法求出的π值只有統計次數足夠多時纔可能準確。
*程序與程序註釋
#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
80.奇數平方的一個有趣性質
編程驗證“大於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);
}
}
81.角谷猜想
日本一位中學生髮現一個奇妙的“定理”,請角谷教授證明,而教授無能爲力,於是產生角谷猜想。猜想的內容是:任給一個自然數,若爲偶數除以2,若爲奇數則乘3加1,得到一個新的自然數後按照上面的法則繼續演算,若干次後得到的結果必然爲1。請編程驗證。
*問題分析與算法設計
本題是一個沿未獲得一般證明的猜想,但屢試不爽,可以用程序驗證。
題目中給出的處理過程很清楚,算法不需特殊設計,可按照題目的敘述直接進行證。
*程序與程序註釋
#include<stdio.h>
void main()
{
int n,count=0;
printf(“Pleaseenter 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);
}
82.四方定理
數論中著名的“四方定理”講的是:所有自然數至多隻要用四個數的平方和就可以表示。
請編程證此定理。
*問題分析與算法設計
本題是一個定理,我們不去證明它而是編程序驗證。
對四個變量採用試探的方法進行計算,滿足要求時輸出計算結果。
*程序與程序註釋
#include<stdio.h>
#include<stdlib.h>
void main()
{
int number,i,j,k,l;
printf(“Pleaseenter 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 anumber = 110
110=7*7+6*6+4*4+3*3
2) Please enter anumber = 211
211=8*8+7*7+7*7+7*7
3) Please enter anumber = 99
99=7*7+5*5+4*4+3*3
83.卡布列克常數
驗證卡布列克運算。任意一個四位數,只要它們各個位上的數字是不全相同的,就有這樣的規律:
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(“Entera number:”);
scanf(“%d”,&n);
vr6174(n);
}
void vr6174(int num)
{
inteach[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)Entera number:9643
[1]:9643-3469=6174
——————————————————————————–
84.尼科徹斯定理
驗證尼科徹斯定理,即:任何一個整數的立方都可以寫成一串連續奇數的和。××
*問題分析與算法設計
本題是一個定理,我們先來證明它是成立的。
對於任一正整數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(“Pleaseenter 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 anumber:13
13*13*13=2197=157+159+161+163+165+167+169+171+173+175+177+179+181Y
2) Please enter anumber:14
14*14*14=2744=183+185+187+189+191+193+195+197+199+201+203+205+207+209Y
*思考題
本題的求解方法是先證明,在證明的過程中找到編程的算法,然後實現編程。實際上我們也可以不進行證明,直接使用編程中常用的試探方法來找出該數列,驗證該定理。請讀者自行設計算法。當然這樣得到的數列可能與用定理方法得到的數列不一樣。
85.迴文數的形成
任取一個十進制整數,將其倒過來後與原來的整數相加,得到一個新的整數後重復以上步聚,則最終可得到一個迴文數。請編程驗證。
*問題分析與算法設計
迴文數的這一形成規則目前還屬於一個猜想,尚未得到數學上的證明。有些迴文數要經歷上百個步聚才能獲得。這裏通過編程驗證。
題目中給出的處理過程很清楚,算法不需要特殊設計。可按照題目的敘述直接進行驗證。
*程序與程序註釋
#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(“Pleaseenetr a number optionaly:”);
scanf(“%ld”,&n);
printf(“Thegeneration 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(“Herewe 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) return1;
else return0;
}
86.自動發牌
一副撲克有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 charn[]={\’2\’,\’3\’,\’4\’,\’5\’,\’6\’,\’7\’,\’8\’,\’9\’,\’T\’,\’J\’,\’Q\’,\’K\’,\’A\’};
inta[53],b1[13],b2[13],b3[13],b4[13];
intb11=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];
elseif(t%4==1) b2[b22++]=a[t-1];
elseif(t%4==2) b3[b33++]=a[t-1];
elseif(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);
}
87.黑白子交換
有三個白子和三個黑子如下圖佈置:
○ ○ ○ . ● ● ●
遊戲的目的是用最少的步數將上圖中白子和黑子的位置進行交換:
● ● ● . ○ ○ ○
遊戲的規則是:(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()
{
intt[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;
}
——————————————————————————–
88.常勝將軍
現有21根火柴,兩人輪流取,每人每次可以取走1至4根,不可多取,也不能不取,誰取最後一楰火柴誰輸。請編寫一個程序進行人機對弈,要求人先取,計算機後取;計算機一方爲“常勝將軍”。
*問題分析與算法設計
在計算機後走的情況下,要想使計算機成爲“常勝將軍”,必須找出取關鍵。根據本題的要求枷以總結出,後走一方取子的數量與對方剛纔一步取子的數量之和等於,就可以保證最後一個子是留給先取子的那個人的。
據此分析進行算法設計就是很簡單的工作,編程實現也十分容易。
*程序與程序註釋
#include<stdio.h>
void main()
{
int a=21,i;
printf(“Gamebegin:\\n”);
while(a>0)
{
do{
printf(“Howmany 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);
}
}
89.搶 30
這是中國民間的一個遊戲。兩人從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(“GameBegin\\n”);
randomize();
if(random(2)==1)
tol=input(tol);
while(tol!=30)
if((tol=copu(tol))==30)
printf(“Ilose! \\n”);
else
if((tol=input(tol))==30)
printf(“Ilose! \\n”);
printf(” * * * * * ** *Game Over * * * * * * * *\\n”);
}
int input(int t)
{
int a;
do{
printf(“Pleasecount:”);
scanf(“%d”,&a);
if(a>2||a<1||t+a>30)
printf(“Errorinput,again!”);
else
printf(“Youcount:%d\\n”,t+a);
}while(a>2||a<1||t+a>30);
returnt+a;
}
int copu(int s)
{
int c;
printf(“Computercount:”);
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;
}
90.搬山遊戲
設有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(“MoreMountain Game\\n”);
printf(“GameBegin\\n”);
pc=cc=0;
g=1;
for(;;)
{
printf(“No.%2dgame \\n”,g++);
printf(“—————————————\\n”);
printf(“How manympuntains are there?”);
scanf(“%d”,&n);
if(!n) break;
printf(“How many mountainsare allowed to each time?”);
do{
scanf(“%d”,&k);
if(k>n||k<1)printf(“Repeat again!\\n”);
}while(k>n||k<1);
do{
printf(“Howmany mountains do you wish movw away?”);
scanf(“%d”,&x);
if(x<1||x>k||x>n)
{
printf(“IIIegal,againplease!\\n”);
continue;
}
n-=x;
printf(“Thereare %d mountains left now.\\n”,n);
if(!n)
{
printf(“……………Iwin. You are failure……………\\n\\n”);cc++;
}
else
{
y=(n-1)%(k+1);
if(!y)y=1;
n-=y;
printf(“Copmputermove %d mountains away.\\n”,y);
if(n)printf(” There are %d mountains left now.\\n”,n);
else
{
printf(“……………Iam failure. You win………………\\n\\n”);
pc++;
}
}
}while(n);
}
printf(“Games intotal have been played %d.\\n”,cc+pc);
printf(“You score iswin %d,lose %d.\\n”,pc,cc);
printf(“My score iswin %d,lose %d.\\n”,cc,pc);
}
——————————————————————————–
91.人機猜數遊戲
由計算機“想”一個四位數,請人猜這個四位數是多少。人輸入四位數字後,計算機首先判斷這四位數字中有幾位是猜對了,並且在對的數字中又有幾位位置也是對的,將結果顯示出來,給人以提示,請人再猜,直到人猜出計算機所想的四位數是多少爲止。
例如:計算機“想”了一個“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()
{
intstime,a,z,t,i,c,m,g,s,j,k,l[4];
long ltime;
ltime=time(NULL);
stime=(unsignedint)ltime/2;
srand(stime);
z=random(9999);
printf(“I have anumber with 4 digits in mind,please guess.\\n”);
for(c=1;;c++)
{
printf(“Entera 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 hanecorrectly guessed %d digits,\\n”,j);
printf(“andcorrectly guessed %d digits in exact position.\\n”,k);
if(k==4)break;
}
printf(“Now you havecorrectly guessed the whole number after %d times.\\n”,c);
}
92.人機猜數遊戲(2)
將以上游戲雙方倒一下,請人想一個四位的整數,計算機來猜,人給計算機提示信息,最終看計算機用幾次猜出一個人“想”的數。請編程實現。
*問題分析與算法設計
解決這類問題時,計算機的思考過程不可能象人一樣具完備的推理能力,關鍵在於要將推理和判斷的過程變成一種機械的過程,找出相應的規則,否則計算機難以完成推理工作。
基於對問題的分析和理解,將問題進行簡化,求解分爲兩個步聚來完成:首先確定四位數字的組成,然後再確定四位數字的排列順序。可以列出如下規則:
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 guessyour number in mind is # # # #.\\n”);
for(i=1;i<10&&k<4;i++)
{
printf(“No.%d:yournumber may be:%d%d%d%d\\n”,++count,i,i,i,i);
printf(“Howmany 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:yournumber may be:%d%d%d%d\\n”,++count,a[0],a[1],a[2],a[3]);
printf(“How many arein 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:Yournumber may be: %d%d%d%d\\n”,++count,a[0],a[1],a[2],a[3]);
printf(“Howmany are in exact positins:”);
scanf(“%d”,&b2);
if(b2==4){prt();flag=0;}
elseif(b2-b1==2)bhdy(j,k);
elseif(b2-b1==-2)
{
c=a[j];a[j]=a[k];a[k]=c;
bhdy(j,k);
}
elseif(b2<=b1)
{
c=a[j];a[j]=a[k];a[k]=c;
}
elseb1=b2;
}
if(flag)printf(“You input error!\\n”);
}
void prt()
{
printf(“Now yournumber must be %d%d%d%d.\\n”,a[0],a[1],a[2],a[3]);
printf(“GameOver\\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;
}
93.漢諾塔
約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,charfromneedle,char toneedle,char usingneedle);
int i=0;
void main()
{
unsigned n;
printf(“please enterthe number of disc:”);
scanf(“%d”,&n);
printf(“\\tneedle:\\ta\\tb\\t c\\n”);
movedisc(n,\’a\’,\’c\’,\’b\’);
printf(“\\t Total:%d\\n”,i);
}
void movedisc(unsigned n,charfromneedle,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);
}
}
94.兎子產子
從前有一對長壽兎子,它們每一個月生一對兎子,新生的小兎子兩個月就長大了,在第二個月的月底開始生它們的下一代小兎子,這樣一代一代生下去,求解兎子增長數量的數列。
*問題分析與算法設計
問題可以抽象成下列數學公式:
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(“Pleaseenter required number of generation:”);
scanf(“%d”,&n);
if(n<3)printf(“\\n Enter error!\\n”);
}
un=un2=1;
printf(“The repidincrease 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 20generation is as felow:
1 1 2 3 5 8 13 21 34 55
89 144 233 377 610 987 1597 2584 4181 6765
95.將阿拉伯數字轉換爲羅馬數字
將大於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 enternumber:”);
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 enternumber:863
863=DCCCLXIII
2. Please enter number:256
256=CCLVI
3. Please enternumber:355
355=CCCLV
4. Please enternumber:522
522=DXXII
5. Please enter number:15
15=XV
*思考題
輸入正整數N,產生對應的英文數字符串並輸出,例如:
1 ONE 2 TWO 3 THREE
10 TEN 11 ELEVEN
135 ONEHUNDRED THIRTY FIVE
——————————————————————————–
96.選美比賽
在選美大獎賽的半決勝賽現場,有一批選手參加比賽,比賽的規則是最後得分越高,名次越低。當半決決賽結束時,要在現場按照選手的出場順序宣佈最後得分和最後名次,獲得相同分數的選手具有相同的名次,名次連續編號,不用考慮同名次的選手人數。例如:
選手序號: 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 NUM7
inta[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;
}
elseif(a[j]==smallest)
l[++k]=j;
for(j=1;j<=k;j++)
m[l[j>=num;
num++;
i=0;
}
printf(“Player-Noscore 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
*思考題
若將原題中的“名次連續編號,不用考慮同名次的選手人數”,改爲”根據同名次的選手人數對選手的名次進行編號“,那麼應該怎樣修改程序。
97.滿足特異條件的數列
輸入m和n(20>=m>=n>0)求出滿足以下方程的正整數數列 i1,i2,…,in,使得:i1+i1+…+in=m,且i1>=i2…>=in。例如:
當n=4, m=8時,將得到如下5 個數列:
51 1 1 4 2 11 3 3 11 3 2 21 2 2 2 2
*問題分析與算法設計
可將原題抽象爲:將M分解爲N個整數,且N個整數的和爲M,i1>=i2>=…>=in。分解整數的方法很低多,由於題目中 有”i1>=i2>=…..>=in,提示我們可先確定最右邊in元素的值爲1,然後按照條件使前一個元素的值一定大於等於當前元 素的值,不斷地向前推就可以解決問題。下面的程序允許用戶選定M和N,輸出滿足條件的所有數列。
*程序與程序註釋
#include<stdio.h>
#define NUM10
inti[NUM];
void main()
{
intsum,n,total,k,flag,count=0;
printf(“Please enterrequried terms(<=10):”);
scanf(“%d”,&n);
printf(“ theirsum:”);
scanf(“%d”,&total);
sum=0;
k=n;
i[n]=1;
printf(“There arefollowing 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;
}
elseif(sum+i[k]>total||k!=1)
{sum-=i[++k]; flag=0;}
elseflag=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 requriedterms(<=10):4
theirsum:8
There are followingpossible series:
[1]: 5111
[2]: 4211
[3]: 3311
[4]: 3221
[5]: 2222
98. 八皇后問題
在一個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()
{
inti,k,flag,not_finish=1,count=0;
i=1;
a[1]=1;
printf(“The possibleconfiguration 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;
elseif(i==1&&a==NUM)
not_finish=0;
elsea++;
}
elseif(a==NUM) a=1;
elsea++;
}
elseif(++i<=NUM)
if(a[i-1]==NUM)a=1;
elsea=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]++;
elsea[NUM-1]=1;
i=NUM-1;
}
}
}
99.超長正整數的加法
請設計一個算法來完成兩個超長正整數的加法。
*問題分析與算法設計
首先要設計一種數據結構來表示一個超長的正整數,然後才能夠設計算法。
首先我們採用一個帶有表頭結點的環形鏈來表示一個非負的超大整數,如果從低位開始爲每個數字編號,則第一位到第四位、第五位到第八位…的每四位組成的數字,依次放在鏈表的第一個、第二個、…結點中,不足4位的最高位存放在鏈表的最後一個結點中,表頭結點的值規定爲-1。例如:
大整數“587890987654321”可用如下的帶表頭結點head的鏈表表示:
按照此數據結構,可以從兩個表頭結點開始,順序依次對應相加,求出所需要的進位後代入下面的運算。具體的實現算法請見程序中的註釋。
*程序與程序註釋
#include<stdio.h>
#include<stdlib.h>
#define HUNTHOU 10000
typedef struct node{ int data;
structnode *next;
}NODE;
NODE *insert_after(NODE *u,intnum);
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;
returns;
}
NODE*inputint(void)
{
NODE *s,*ps,*qs;
struct number {int num;
structnumber *np;
}*p,*q;
int i,j,k;
long sum;
char c;
p=NULL;
while((c=getchar())!=\’\\n\’)
if(c>=\’0\’&&c<=\’9\’)
{
q=(structnumber *)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{
inti,k=HUNTHOU;
for(i=1;i<=4;i++,k/=10)
putchar(\’0\’+s->next->data%(k)/(k/10));
}
}
}
100. 數字移動
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>
inta[]={0,1,2,5,8,7,6,3};
intb[9];
intc[9];
intcount=0;
void main()
{
int i,j,k,t;
void print();
printf(“Please enteroriginal order of digits 1~8:”);
for(i=0;i<8;i++)
scanf(“%d”,&b[a[i>);
printf(“The sorting processis 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;
}
elseif(b[c[j>==i) break;
}
voidprint(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++);
}
——————————————————————————–