失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > C语言实现野人与传教士过河问题

C语言实现野人与传教士过河问题

时间:2022-03-06 22:40:55

相关推荐

C语言实现野人与传教士过河问题

野人与传教士过河问题

问题重述:

有三个传教士和三个野人过河, 只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于传教士的人数,那么传教士就会有危险,采用何种渡河方法,可以安全过河。

算法分析:

初始状态:左岸,3野人,3传教士;右岸, 0野人,0传教士;船停在左岸,船上有0个人。

目标状态:左岸,0野人,0传教士;右岸, 3野人,3传教士;船停在右岸,船上有0个人。

将整个问题抽象成怎样从初始状态经一系列的中间状态从而达到目标状态,状态的改变是通过划船渡河来引发的。

根据要求,共得出以下5中可能的渡河方案:

(1)渡2传教士

(2)渡2野人

(3)渡1野人1传教士

(4)渡1传教士

(5)渡1野人

本程序使用类来定义状态结点,使用集合存储状态结点,使用递归的思想来寻找目标状态。

程序详细执行流程如下:

首先,包含状态(首次为初始状态)的结构体结点(已存入结构体数组)传入处理函数,然后判断该传入结点状态是否为目标状态,是则遍历打印结构体数组,打印完成之后,返回递归调用处,顺序执行之后代码(此步骤关系到是否能找到所有过河路径);否则继续判断是否该传入结点已存在于结构体数组当中,如存在,不再往下执行,返回递归调用处,顺序执行之后代码;若不存在,则继续判断该传入状态的人数是否合理(是否出现人物数量小于0的情况等),若不合理,返回递归调用处,顺序执行之后代码;若合理,则继续判断传教士和野人人数限制条件,即在传教士人数不为0的情况下,野人人数是否大于传教士人数,若大于则出现吃人的情况,也就是说该传入状态也不合理,则返回递归调用处,顺序执行之后代码;若不满足大于条件,则说明该状态是路径转态,也就是合理的,那么进行五种渡河方案的依次变换,首先为第一种渡河方案,两个传教士过河(注意:此处的5中渡河方案没有固定顺序,也可以是其他渡河方案),那么对该传入状态的左岸和右岸的传教士人数和野人人数进行增减(若为左岸到右岸,则左岸人数减,右岸人数加,此处有一个小技巧见本段末尾)。增减完成并改变船的状态(使用正负一表示,正一为左岸,负一为右岸)以后就产生了一个新的状态,将该状态存入结构体数组,之后此处又递归调用处理函数,将新产生的转态结点传入,再次进行上述条件限制判断。若在该判断途中被返回至递归调用处,说明该状态不合理,则此时将已经存入结构体数组的状态结点移出结构体数组,然后程序顺序执行,进行下一个渡河方案的处理,也就是说,此时的处理是对上一个传入结点的操作(因为刚传入的已经移出了);若在判断途中未被返回至递归调用处,也就是说,传入的结点合理了,那么又开始从第一种渡河方案开始对该传入状态进行操作。按照上述过程循环执行,直到出现目标状态,回到本段开头,遍历结构体数组,打印渡河路径结点信息。完成以后,返回递归调用处,顺序执行之后代码,此后的操作是在寻找其他渡河路径。原理为:由于该处理函数末尾存在return语句(关键),所以在找到目标状态并返回之后,目标转态结点同样会被移出结构体数组,然后在其上一个结点开始顺序往下执行操作之后的一种渡河方案,查看是否在该结点处,还有其他渡河方案可以达到目标状态,若有则同样按上述方法执行(打印输出),若执行完后面的所有渡河方案,发现都没有能够达到目标状态的结点,则会执行末尾的返回语句,返回之后,该状态结点也会被移除(关键),那么此时操作的状态结点就是上上个结点状态,对其进行其后的渡河方案操作。按照此法,不断往后退,直到所有结点都被移除,此时说明已经完成所有渡河路径的搜索(深度)。至此,本程序的执行过程叙述完毕。

小技巧:从左岸到右岸,和从右岸到左岸的状态变化是不一样的,前者左岸的人数减,右岸的人数加;后者左岸的人数加,右岸的人数减。我们不应单独再写程序来处理,而是应该使用船的转态带入计算来处理,注意,此技巧在于船的转态使用正负一来表示,而不应该是1和0,以及其他表示方法。为什么这么说?因为任何数乘以一,其本身都不会改变(有我也不会承认)。而正负号在此起到关键作用,我们使用正负一去乘以五种渡河方案的改变数值,从而得到的就是我们变换的正确结果,不论左岸右岸,都是正确合理的。

完整代码:

#include <stdio.h>#define MAX 100//状态struct zt{int left_c;int right_c;int left_y;int right_y;int boat_location;};struct zt ztarr[MAX];int index=0;int numpass=0;int start_c,start_y;int handle(zt t){//是否达到目标转态if(t.right_c == start_c && t.right_y == start_y){numpass++;printf("\n找到第%d条路径!\n",numpass);printf("左传\t左野\t右传\t右野\t船\n");for(int i = 0; i <= index ; i++){printf("%2d\t",ztarr[i].left_c);printf("%2d\t",ztarr[i].left_y);printf("%2d\t",ztarr[i].right_c);printf("%2d\t",ztarr[i].right_y);printf("%2d\t",ztarr[i].boat_location);printf("\n");}//找到多条路径的关键一return 0;}//是否重复操作for(int i = 0; i < index; i++){if(t.left_c == ztarr[i].left_c && t.left_y == ztarr[i].left_y){if(t.boat_location == ztarr[i].boat_location){return 0;}}}//人数是否合理吗if(t.left_c < 0 || t.left_y < 0 || t.right_c < 0 || t.right_y < 0 ){return 0;}//传教士是否被吃if((t.left_c < t.left_y && t.left_c != 0) || (t.right_c < t.right_y && t.right_c != 0) ){return 0;}//定义一个临时节点struct zt tt;//两个传教士过河tt.left_c = t.left_c - 2 * t.boat_location;tt.left_y = t.left_y;tt.right_c = t.right_c + 2 * t.boat_location;tt.right_y = t.right_y;tt.boat_location = ( -t.boat_location);index = index + 1;ztarr[index] = tt;handle(ztarr[index]);index = index - 1;//两个野人过河tt.left_c = t.left_c;tt.left_y = t.left_y - 2 * t.boat_location;tt.right_c = t.right_c ;tt.right_y = t.right_y + 2 * t.boat_location;tt.boat_location = ( -t.boat_location);index = index + 1;ztarr[index] = tt;handle(ztarr[index]);index = index-1;//一个野人,一个传教士过河tt.left_c = t.left_c - 1 * t.boat_location;tt.left_y = t.left_y - 1 * t.boat_location;tt.right_c = t.right_c + 1 * t.boat_location;tt.right_y = t.right_y + 1 * t.boat_location;tt.boat_location = ( -t.boat_location);index = index + 1;ztarr[index] = tt;handle(ztarr[index]);index = index-1;//一个传教士过河tt.left_c = t.left_c - 1 * t.boat_location;tt.left_y = t.left_y;tt.right_c = t.right_c + 1 * t.boat_location;tt.right_y = t.right_y;tt.boat_location = ( -t.boat_location);index = index + 1;ztarr[index] = tt;handle(ztarr[index]);index = index-1;//一个野人过河tt.left_c = t.left_c;tt.left_y = t.left_y - 1 * t.boat_location;tt.right_c = t.right_c;tt.right_y = t.right_y + 1 * t.boat_location;tt.boat_location = ( -t.boat_location);index = index + 1;ztarr[index] = tt;handle(ztarr[index]);index = index-1;//找到多条路径的关键二return 0;}void main(){printf("请输入初始传教士人数:");scanf("%d",&start_c);printf("请输入初始传教士人数:");scanf("%d",&start_y);ztarr[index].left_c = start_c;ztarr[index].left_y = start_y;ztarr[index].right_c = 0;ztarr[index].right_y = 0;ztarr[index].boat_location = 1;handle(ztarr[index]);printf("已为您找到%d条过河路径!并且已全部加载完毕!\n",numpass);}

结果图:

下面是5个传教士和3个野人的情况:

如有错误,欢迎指正!_

后续补充说明,本程序最初使用 vc++6.0 编译运行通过,如果在 vs 上编译出现

① unknown type name"zt" 的错误,请申明其类型为 struct:

② C4996 错误,请将 scanf 需要修改为 scanf_s:

特此说明![ 年 5 月13 日 第一次补充]

如果觉得《C语言实现野人与传教士过河问题》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。