printf("*");
/*控制打印左侧的*号*/
for(;x<62-m;x++)printf("");
printf("*\n");
/*控制打印同一行中对称的右侧*号*/
}
}break;
default:
printf("谢谢!
\n");return0;
}
}
}
【编程小结】
在本程序中使用到系统函数库中的库函数asin(),acos(),大大加快编程效率,增强实现效果,但在使用库函数过程中应该及时将其所属的头文件包含进来。
本任务是一个典型的使用库函数的例子。
任务7.2:
杨辉三角形
【问题描述】在屏幕上打印输出杨辉三角形,如下图所示。
图:
13行杨辉三角图
【问题分析与算法设计】
杨辉三角形中的数,正是(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
intc(int,int);
voidmain()
{
inti,j,n=0;
printf("N=");
scanf("%d",&n);/*控制输入正确的值以保证屏幕显示的图形正确*/
for(i=0;i<=n;i++)/*控制输出N行*/
{
for(j=0;j<24-2*i;j++)printf("");/*控制输出第i行前面的空格*/
for(j=1;j
printf("\n");
}
printf("\n");
}
intc(intx,inty)/*求杨辉三角形中第x行第y列的值*/
{
intz;
if((y==1)||(y==x+1))return1;/*若为x行的第1或第x+1列,则输出1*/
z=c(x-1,y-1)+c(x-1,y);/*否则,其值为前一行中第y-1列与第y列值之和*/
returnz;
}
【编程小结】
1.算法设计和实现均严格围绕杨辉三角的数字规律,即从起始行算起的第3行开始杨辉三角中除了最外层(不包括杨辉三角底边)的数为1外,其余的数都是它肩上两个数之和;
2.在程序中使用了自定义的递归函数intC(int,int)。
递归是一项非常重要的编程技巧,很多程序中都或多或少的使用了递归函数。
递归的意思就是函数自己调用自己本身,或者在自己函数调用的下级函数中调用自己。
递归实际上包含递推和回归两个过程,初学者往往只能判断到递推,对回归的去向和步骤往往把握不准。
简单说,递归是一个从未知逐层递推到已知,从已知逐层回归求解未知的过程。
3.程序在所有函数之前对自定义函数给予原型声明,方便后面函数的调用。
在调用函数intC(int,int)时,将实参i和j的值分别传递给形参x和y,执行完后又通过参数z返回调用函数。
任务8.1:
黑白棋子交换
【问题描述】
有三个白子和三个黑子如下图布置:
○
○
○
.
●
●
●
游戏的目的是用尽可能少的步数将上图中白子和黑子的位置进行交换:
●
●
●
.
○
○
○
游戏的规则是:
(1)一次只能移动一个棋子;
(2)棋子可以向空格中移动,也可以跳过一个对方的棋子进入空格,但不能向后跳,也不能跳过两个子。
请用计算机实现上述游戏。
【问题分析与算法设计】
计算机解决此类问题的关键是要找出问题的规律。
分析本题,可总结出以下规则:
(1)黑子向左跳过白子落入空格,转(5)
(2)白子向右跳过黑子落入空格,转(5)
(3)黑子向左移动一格落入空格(但不应产生棋子阻塞现象),转(5)
(4)白子向右移动一格落入空格(但不应产生棋子阻塞现萌),转(5)
(5)判断游戏是否结束,若没有结束,则转
(1)继续。
所谓的“阻塞”现象就是:
在移动棋子的过程中,两个尚未到位的同色棋子连接在一起,使棋盘中的其它棋子无法继续移动。
例如按下列方法移动棋子:
star:
○
○
○
.
●
●
●
step1:
○
○
○
●
.
●
●
或step1:
○
○
.
○
●
●
●
✧step2:
出现白、空、黑、白
○
○
.
●
○
●
●
✧或step2:
出现黑、白、空、黑
○
○
●
○
.
●
●
step3:
○
○
●
.
○
●
●
step4:
两个●连在一起产生阻塞
○
○
●
●
○
.
●
或step4:
两个白连在一起产生阻塞
○
.
●
○
○
●
●
产生阻塞的现象的原因是在第2步时,棋子○不能向右移动,只能将●向左移动。
总结产生阻塞的原因,当棋盘出现“黑、白、空、黑”或“白、空、黑、白”状态时,不能向左或向右移动中间的棋子,只移动两边的棋子。
按照上述规则,可以保证在移动棋子的过程中,不会出现棋子无法移动的现象,且可以用最少的步数完成白子和黑子的位置交换。
【代码实现】
#include
intnumber;
voidprint(inta[]);
voidchange(int*n,int*m);
voidmain()
{
intt[7]={1,1,1,0,2,2,2};/*初始化数组1:
白子2:
黑子0:
空格*/
inti,flag;
print(t);
while(t[0]+t[1]+t[2]!
=6||t[4]+t[5]+t[6]!
=3)/*判断游戏是否结束。
若还没有完成棋子的交换则继续进行循环*/
{
flag=1;/*flag为棋子移动一步的标记1:
尚未移动棋子
0:
已经移动棋子*/
for(i=0;flag&&i<5;i++)/*若白子可以向右跳过黑子,则白子向右跳*/
if(t[i]==1&&t[i+1]==2&&t[i+2]==0)
{change(&t[i],&t[i+2]);print(t);flag=0;}
for(i=0;flag&&i<5;i++)/*若黑子可以向左跳过白子,则黑子向左跳*/
if(t[i]==0&&t[i+1]==1&&t[i+2]==2)
{change(&t[i],&t[i+2]);print(t);flag=0;}
for(i=0;flag&&i<6;i++)/*若向右移动白子不会产生阻塞,则白子向右移动*/
if(t[i]==1&&t[i+1]==0&&(i==0||t[i-1]!
=t[i+2]))
{change(&t[i],&t[i+1]);print(t);flag=0;}
for(i=0;flag&&i<6;i++)/*若向左移动黑子不会产生阻塞,则黑子向左移动*/
if(t[i]==0&&t[i+1]==2&&(i==5||t[i-1]!
=t[i+2]))
{change(&t[i],&t[i+1]);print(t);flag=0;}
}
}
voidprint(inta[])
{
inti;
printf("No.%2d:
.............................\n",number++);
printf("");
for(i=0;i<=6;i++)
printf("|%c",a[i]==1?
'*':
(a[i]==2?
'@':
''));
printf("|\n.............................\n\n");
}
voidchange(int*n,int*m)
{
intterm;
term=*n;*n=*m;*m=term;
}
【编程小结】
1.程序中change(int*,int*)是一个返回值为空,带有两个整形指针变量参数的自定义函数。
其功能是交换两个变量的值,所传递的是变量地址即地址传递,在传递两个实型参数内存首地址的同时也将实参的“实际控制权”交给了形式参数。
2.数组t用于模拟棋盘,其中元素值为1代表白子,2代表黑子;设定t[0]+t[1]+t[2]==3,t[4]+t[5]+t[6]==6为初始状态即从左至右分别为3个白子,1个空格,3个黑子;最终状态左至右分别为3个黑子,1个空格,3个白子即以t[0]+t[1]+t[2]==6,t[4]+t[5]+t[6]==3表示;屏幕显示时以‘*’表示白子,‘@’表示黑子。
3.程序核心代码是主函数main()中的while循环,在其循环体内分成两类状态处理分别为白(黑)子可以跳过黑(白)子,此时交换的是t[i]和t[i+2]两个元素值,i<5;当向右移动白子或向左移动黑子不产生阻塞,此时交换的是t[i]t[i+1]两个元素值,i<6;
4.程序中flag作为工作变量用于标记是否发生交换。
5.程序执行结果如下图
任务8.2:
班干部值日安排
【问题描述】
班主任要求班干部要轮流值日负责班级卫生和上课考勤。
班上有班长,团支书,生活委员,学习委员,体育委员,宣传委员,纪律委员七位班干部,分别用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
#include
char*leader(char);
inta[8];
char*day[]={"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
/*建立星期表*/
voidmain()
{
inti,j,t;
a[4]=6;/*星期四是F值日*/
for(i=1;i<=3;i++)
{
a[i]=2;/*假设B值日的日期*/
if(!
a[i+3])a[i+3]=7;/*若三天后无人值日则安排G值日*/
else{a[i]=0;continue;}/*否则B值日的日期不对*/
for(t=1;t<=3;t++)/*假设E值日的时间*/
{
if(!
a[t])a[t]=5;/*若当天无人值日则安排E值日*/
elsecontinue;
if(!
a[t+2])a[t+2]=4;/*若E值日两天后无人值日则应为D*/
else{a[t]=0;continue;}/*否则E值日的日期不对*/
for(j=5;j<7;j++)
{
if(!
a[j])a[j]=3;/*若当天无人值日,则安排C值日*/
elsecontinue;
if(!
a[j+1])a[j+1]=1;/*C之后一天无人值日则应当是A值日*/
else{a[j]=0;continue;}/*否则A值日日期不对*/
printf("------班级值日表------\n");
for(i=1;i<=7;i++)/*安排完毕,调用leader()函数,输出结果*/
printf("%10.8s:
%s值日.\n",leader('A'+a[i]-1),day[i]);
exit(0);
}
}
}
}
char*leader(charch)
{
switch(ch)
{
case'A':
return"班长";break;
case'B':
return"团支书";break;
case'C':
return"生活委员";break;
case'D':
return"学习委员";break;
case'E':
return"体育委员";break;
case'F':
return"宣传委员";break;
case'G':
return"纪律委员";break;
}
}
【编程小结】
1.程序在实现值日安排时采用的穷举法,数组a作为工作数组,所起到的作用是用于保存值日安排,其与数组day共同点是数组下标均表示相同的星期。
2.数组day是一个指针数组,除首元素外每个元素均指向初始化时的一个字符串常量的首地址。
3.数组a的元素值为整形数1至7分别对应七位同学。
4.为使实际星期表示与数组下标相一致,数组a和数组day中下标为0的数组元素空闲。
5.自定义函数leader()的作用是实现通过A~F转换成班干部的具体职位输出,该函数返回值为字符指针即班干部职位字符串的存储首地址。
6.
为美化值日表输出格式,程序最后的printf("%10.8s:
%s值日.\n",leader('A'+a[i]-1),day[i]);函数中的第一个输出格式为%10.8s,其中采用的修饰符作用为给出十个字节的输出位置,但实际允许输出八个字节即四个汉字字符,输出结果见下图。
班级值日表输出结果
任务9.1:
三天打鱼两天晒网
有一位渔夫从2000年1月1日起开始“三天打鱼两天晒网”,问此人在以后的某一天中是“打鱼”还是“晒网”。
【问题分析与算法设计】
根据题意可以将解题过程分为三步:
1)计算从2000年1月1日开始至指定日期共有多少天;
2)由于“打鱼”和“晒网”的周期为5天,所以将计算出的天数用5去除;
3)根据余数判断他是在“打鱼”还是在“晒网”;若余数为1,2,3,则他是在“打鱼”,否则是在“晒网”。
在这三步中,第一步最关键。
求从2000年1月1日至指定日期有多少天,要判断经历年份中是否有闰年,二月为29天,平年为28天。
闰年的方法是:
年份能被4整除但不能被100整除或能被400整除,则为闰年。
计算相隔天数使用当年前一年的12月31日距离2000年1月1日的天数加上当年从1月1日开始的天数。
【代码实现】
#include
intdays(structdateday);
structdate{
intyear;
intmonth;
intday;
};
voidmain()
{
structdatetoday,term;
intyearday,year,day;
printf("EnterDate:
(forexample:
year/month/day)");
scanf("%d/%d/%d",&today.year,&today.month,&today.day);/*输入当年日期*/
term.month=12;/*设置指定年前一年变量的初始值:
月*/
term.day=31;/*设置指定年前一年变量的初始值:
日*/
for(yearday=0,year=2000;year{
term.year=year;
yearday+=days(term);/*计算从2000年至指定年的前一年共有多少天*/
}
yearday+=days(today);/*加上指定年中到指定日期的天数*/
day=yearday%5;/*求余数*/
if(day>0&&day<4)printf("fishingday.\n");/*打印结果*/
elseprintf("sleepingday.\n");
}
intdays(structdateday)
{
staticintday_tab[13]=
{0,31,0,31,30,31,30,31,31,30,31,30,31};/*平均每月的天数*/
inti,leap;
leap=day.year%4==0&&day.year%100!
=0||day.year%400==0;
/*判定year为闰年还是平年,leap=0为平年,非0为闰年*/
if(leap==0)day_tab[2]=28;
elseday_tab[2]=29;
for(i=1;iday.day+=day_tab[i];
returnday.day;
}
【编程小结】
1.程序中首先声明结构体类型date,含有year,month,day三个成员,分别用于记录年、月、日。
结构体是自定义构造类型,当数据需要多个属性时则需使用该构造类型。
2.为配合程序中printf("EnterDate:
(forexample:
year/month/day)");函数输入提示,scanf("%d/%d/%d",&today.year,&today.month,&today.day);函数采用‘/’作为间隔符,输入当年日期。
3.程序中定义了today,term两个结构体类型变量分别用于保存指定年日期和其前一年日期,term.month=1