传教士与野人过河问题.docx
《传教士与野人过河问题.docx》由会员分享,可在线阅读,更多相关《传教士与野人过河问题.docx(21页珍藏版)》请在冰豆网上搜索。
传教士与野人过河问题
贵州航天职业技术学院
《C语言程序设计》
系别:
__计算机科学系_________
班级:
__10级软件技术_________
姓名:
_______________________
********陆树芬_______________
小组成员:
___翟奇张源李雪_______
前言
C语言作为一门最通用的语言,在过去很流行,将来依然会如此。
几乎每一个理工科或者其他专业的学生毫不例外地要学习它。
从C语言产生到现在,它已经成为最重要和最流行的编程语言之一。
在各种流行编程语言中,都能看到C语言的影子,如Java的语法与C语言基本相同。
学习、掌握C语言是每一个计算机技术人员的基本功之一。
C语言具有高级语言的强大功能,却又有很多直接操作计算机硬件的功能(这些都是汇编语言的功能),因此,C语言通常又被称为中级语言。
学习和掌握C语言,既可以增进对于计算机底层工作机制的了解,又为进一步学习其他高级语言打下了坚实的基础。
本大作业是对学生在课堂上所学知识的一次综合检测。
通过本次大作业的制作,应能综合使用在《C语言程序设计》课程中学到的多种基础知识,并可以很好的应用到实际操作中去,具备简单的项目设计能力。
本大作业主要以传教士和野人怎样从河的一岸完全渡到另一岸,而不发生野人吃掉传教士这一问题展开,主要使用C语言中的链表应用完成程序的设计,将此次程序设计报告分为以下几点:
1.问题的描述:
描述本程序的设计内容;
2.问题分析:
介绍此程序设计的结构和算法分析
3.程序设计流程图
4.程序各个功能模块的实现
5.程序运行结果:
将各种情况下的运行结果通过屏幕截取下来
6.程序设计结论:
描述本次程序设计的一些体会和在程序设计过程中所获得的一些知识以及本次程序设计所存在的不足和缺陷。
7.完成本次程序设计所翻阅的资料
一.问题描述
有M个传教士和N个野人来到河边准备渡河,河岸有一条船,每次至多可供k人乘渡。
任何时刻在河的两岸以及船上的野人数目总是不超过传教士的数目。
二.问题分析
1.定义节点的结构:
以取船的一个来回作为一步搜索,这样节点可由下面几个量进行描述:
两岸的传教士人数和野人人数、船上的传教士人数和野人人数,由初始节点搜索几步后到达本节点。
需要注意的是并不是所有节点都是可达的,题目中对可达节点作出了限制,只有两岸上的人数必须不能为负,且每一时刻野人的人数都必须小于或等于传教士的人数。
2.定义连接:
两个节点间的连接可由般从右到左岸时船上的传教士人数、野人人数、船返回时船上的传教士人数、野人人数进行描述。
3.算法分析:
先来看看问题的初始状态和目标状态,假设传教士和野人人数都为3人,船的负载人数为2人,河岸分为右岸和左岸:
初始状态:
右岸3传教士,3野人;
左岸0传教士,0野人;
船停在右岸,船上有0个人;
目标状态:
右岸0传教士,0野人;
左岸3传教士,3野人;
船停在左岸,船上有0个人;
整个问题就抽象成了怎样从初始状态经中间的一系列状态达到目标状态。
问题状态的改变是通过划船渡河来引发的,所以合理的渡河操作就成了通常所说的算符,根据题目要求,可以得出以下5个算符(按照渡船方向的不同,也可以理解为10或更多个算符):
渡1野人、渡1传教士、渡1野人1传教士、渡2野人、渡2传教士
算符知道以后,剩下的核心问题就是搜索方法:
首先定义freeit()函数释放不符合条件的节点,determ()函数判断符合条件的情况,sign()函数判断船的航行方向,copyit()函数复制节点内容,print()函数显示保存结果,然后在trans()函数中通过调用sign()、copit()、determ()、print()完成渡河既搜索方法。
搜索中采用的一些规则如下:
1、渡船优先规则:
右岸一次运走的人越多越好(即右岸运多人优先),同时野人优先运走;左岸一次运走的人越少越好(即左岸运少人优先),同时传教士优先运走;
2、不能重复上次渡船操作(通过链表中前一操作比较),避免进入死循环;
3、任何时候河两边的野人和传教士人数均分别大于或等于0且小于或等于4;
4、由于只是找出最优解,所以当找到某一算符(当前最优先的)满足操作条件后,不再搜索其下一节点,而是直接载入链表。
5、若搜索某节点a的时候,没有找到合适的子节点,则从链表中返回节点a的父节点b,从上次已经选择了的算符之后的算符中找最优先的算符继续搜索b
三.主要流程图
四.基本数据类型声明及数据结构
#include
#include
#include
#include
FILE*fp;
structa*jj,head;//jj指向各种情况下船上的传教士和野人人数
longr,total=0,js=0;/*js渡船的方法数,r为船的负载人数,total为初始状态到目标状态之间的状态个数*/
longk1,k2;//起初传教士和野人的个数
/*结构类型a:
记录各种情况下船上的传教士和野人数*/
structa{
longm,s;//船上的传教士和野人个数
structa*next;//指向下一个接点(a)的指针next
};
/*建立双向的指针链表,记入符合的情况*/
structaim
{
longm1,s1;//右岸的传教士和野人个数
longm2,s2;//左岸的传教士和野人个数
longn;//n为摆渡次数
structaim*back,*next;//指向前一个接点(aim)的指针bake和下一个接点(aim)的指针next
};
五.程序主要实现说明及设置
1./*释放该接点,并将其上的接点的next指针还原*/
voidfreeit(structaim*p)
{
structaim*p1=p;
p1=p->back;
free(p);
if(p1!
=NULL)
p1->next=NULL;
return;
}
2./*判断函数判断符合条件的情况*/
longdeterm(structaim*p)
{
structaim*p1=p;
if(p->s1>k2||p->m1>k1||p->s2>k2||p->m2>k1||p->s1<0||p->s2<0||p->m1<0||p->m2<0)
return-1;
if(k1>4||k2>4)
return-1;
if(p->m1!
=0)
if(p->s1>p->m1)
return-1;
if(p->m2!
=0)
if(p->s2>p->m2)
return-1;
while(p1!
=NULL)
{
p1=p1->back;
if(p1!
=NULL)
if(p1->n%2==p->n%2)
if(p1->s1==p->s1)
if(p1->s2==p->s2)
if(p1->m1==p->m1)
if(p1->m2==p->m2)
return-1;
}
if(p->s1==0&&p->m1==0)//条件成立摆渡完成返回1
if(p->n%2==0)
return1;
else
return-1;
return0;
}
3./*符号函数判断是从右到左航行还是从左到右航行*/
longsign(longn)
{
if(n%2==0)
return-1;//从右到左再返回
return1;//从右到左不反回
}
4./*复制内容函数*/
voidcopyit(structaim*p3,structaim*p)
{
p3->s1=p->s1;
p3->s2=p->s2;
p3->m1=p->m1;
p3->m2=p->m2;
p3->n=p->n+1;//指向下一次摆渡
p3->back=p;
p3->next=NULL;
p->next=p3;
}
5./*打印函数在屏幕上显示结果并将其存入文件中*/
voidprint(structaim*p3)
{
structaim*p=p3;
js++;
while(p->back)
{
p=p->back;//指向下一个方法的接点
}
/*********************************/
printf("\n方法%ld:
\n",js);
fprintf(fp,"\n方法%ld:
\n",js);
/*********************************/
while(p)
{
/*********************************/
printf("%ld,%ld:
:
%ld,%ld\t",p->m1,p->s1,p->m2,p->s2);
fprintf(fp,"%ld,%ld:
:
%ld,%ld\t",p->m1,p->s1,p->m2,p->s2);
/*********************************/
p=p->next;//指向下一个节点
}
printf("\n");
}
6./*转移函数将人从右岸渡到左岸*/
voidtrans(structaim*p)
{
structaim*p3;
structa*fla;
longi,j,f,e;
fla=&head;
p3=(structaim*)malloc(sizeof(structaim));
f=sign(p->n);
for(i=0;i{
fla=fla->next;
copyit(p3,p);
/*渡河:
右岸人数减去船上所渡人数,左岸人数则加上从右岸渡来的人数*/
p3->s1-=fla->s*f;
p3->m1-=fla->m*f;
p3->s2+=fla->s*f;
p3->m2+=fla->m*f;
j=determ(p3);//调用判断函数判断符合条件的情况
if(j==-1)
{
if(i{
continue;
}
else
{
freeit(p3);//调用freeit()释放该接点
break;
}
}
if(j==1)
{
if(i{
print(p3);
continue;
}
else
{
print(p3);//调用print()打印结果
freeit(p3);
break;
}
}
if(j==0)
trans(p3);//递归调用转移函数将人从右岸渡到左岸
}
if(j==-1)
/*********************************/
printf("你输入的数据出现错误!
\n");
printf("请重新运行程序!
\n");
/*********************************/
return;
}
7./*程序入口*/
voidmain()
{
structaim*p,*p1;
longj,a;
longe,f;
structa*flag;
FILE*fpt;
if((fpt=fopen("c:
result.txt","w+"))==0)
{
printf("can'tcreatit\n");
exit(0);
}
fp=fpt;
p=(structaim*)malloc(sizeof(structaim));
p->back=NULL;
p->next=NULL;
p->s2=0;
p->m2=0;
p->n=1;
/*********************************/
printf("请输入船的负载人数\n");
fprintf(fp,"\n请输入船的负载人数\n");
scanf("%ld",&r);
fprintf(fp,"\n%ld\n",r);
/*********************************/
/*if(r>3)
{
printf("船太小已经超载了!
\n");
}*/
flag=&head;
for(e=0;e<=r;e++)
for(f=0;f<=r;f++)
if(e+f>0&&e+f<=r)
{
total++;
jj=(structa*)malloc(sizeof(structa));
jj->m=e;
jj->s=f;
flag->next=jj;
jj->next=NULL;
flag=jj;
}
/*********************************/
printf("注意:
输入的传教士的人数大于或等于野人个数:
\n");
printf("请输入传教士和野人的个数:
传教士,野人;\n");
fprintf(fp,"\n请输入传教士和野人的个数:
传教士,野人\n");
scanf("%ld,%ld",&p->m1,&p->s1);
fprintf(fp,"\n%ld,%ld\n",p->m1,p->s1);
/**********************************/
k1=p->m1;
k2=p->s1;
if(k1>4||k2>4||k2>k1)
{
printf("你输入的数据出现错误!
\n");
printf("请重新运行程序!
\n");
fprintf(fp,"\n你输入的数据出现错误!
\n");
fprintf(fp,"\n请重新运行程序!
\n");
}
trans(p);
fclose(fpt);
getch();
}
六.运行结果
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
七.实验结论
____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
八.参考文献
(1).李凤霞《C语言程序设计教程》北京理工大学出版社
(2).刘枧张宜坤《C语言程序设计》人民邮电出版社
(3).贾若刘枧《C语言程序设计上机指导教程》
人民邮电出版社
附录
C语言是一门计算机基础语言,通过这一学期的学习,对于它的一些特点和算法有了一定程度的了解,并能够做一些中等的题目,但并没有完全掌握。
这一周的课程设计正是让我们巩固以前的知识并在此基础上能够有所突破。
这次做的野人和传教士过河问题,对于我来说有一定困难,因为我的C语言基础并不是很过硬,所以在一开始我的程序并没有太大的进展,除了知道问题的关键要求外就没有一点头绪,只能看着问题发呆。
后来我查阅了很多资料和一些相关的程序设计思想和实例源码,才渐渐有了一些头绪。
要完成过河问题并且不能发生吃人事件,首先要建立两个链表:
结构类型a记录各种情况下船上的传教士和野人数和双向的指针链表aim记入符合渡船的情况。
要完成渡河就要考虑怎样从初始状态经中间的一系列状态达到目标状态所以合理的渡河操作就成了通常所说的算符,通过搜索算符的方法完成渡河问题。
搜索的方法是:
首先定义freeit()函数释放不符合条件的节点,determ()函数判断符合条件的情况,sign()函数判断船的航行方向,copyit()函数复制节点内容,print()函数显示保存结果,然后在trans()函数中通过调用sign()、copit()、determ()、print()完成渡河,而具体的渡河方法是:
右岸人数减去船上所渡人数,左岸人数则加上从右岸渡来的人数。
在设计的过程中遇到很多问题,可以说得是困难重重,但毕竟第一次做大的程序设计,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对一些前面学过的知识理解得不够深刻,掌握得不够牢固,比如说本程序所用到的结构体,指针,链表……通过这次课程设计之后,我们把前面所学过的知识又重新温故了一遍。