修道士与野人问题
修道士与野人问题

.修道士与野人问题这是一个古典问题。
假设有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数(除非修道士个数为0)。
如果两种人都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
要求:(1)用一个三元组(x1,x2,x3)表示渡河过程中各个状态。
其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
例如(2,1,1)表示起始岸上有两个修道士,一个野人,小船在起始岸一边。
采用邻接表做为存储结构,将各种状态之间的迁移图保存下来。
(2)采用广度搜索法,得到首先搜索到的边数最少的一条通路。
(3)输出数据若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态←…中间状态←…初始状态。
若问题无解,则给出“渡河失败”的信息。
(4)求出所有的解。
1.需求分析有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数,否则修道士就会有危险,设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
用三元组(x1,x2,x3)来表示渡河过程中各个状态,其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态←…中间状态←…初始状态,若问题无解,则给出“渡河失败”的信息。
2.设计2.1 设计思想(1)数据结构设计逻辑结构设计: 图型结构存储结构设计: 链式存储结构采用这种数据结构的好处:便于采用广度搜索法,得到首先搜索到的边数最少的一条通路,输出一个最佳方案,采用图的邻接表存储结构搜索效率较高。
修道士和野人问题

修道⼠和野⼈问题 休闲时刻看看神经⽹络⽅⾯的书,发现了修道⼠和野⼈的问题,不禁勾引起我写算法的欲望,曾经的三只⼤⽼虎三只⼩⽼虎过河问题、⼈狼⽺⽩菜过河问题、汉诺塔、哈夫曼等等各种算法瞬间在脑海中约隐约现,修道⼠和野⼈问题我以前好像没有解开,中午吃饭的时候在脑海中重新构造思路,下午耗了点时间把它⼲掉。
(算法不在代码⾥,⽽在思想中;所以尽量不要看我的代码,⽽要仔细分析我写的思路) 题⽬: 设有3个修道⼠和3个野⼈来到河边,打算⽤⼀条船从河的左岸渡到河的右岸。
但该船每次只能装载两个⼈,在任何岸边野⼈的数⽬都不得超过修道⼠的⼈数,否则修道⼠就会被野⼈吃掉。
假设野⼈服从任何⼀种过河安排,请问如何规划过河计划才能把所有⼈都安全地渡过河去。
⾸先考虑总共有(3+1)*(3+1)= 16 种不同的状态(因为左岸可以有0,1,2,3个传教⼠,左岸可以有0,1,2,3个野⼈),所以可以考虑使⽤穷举法。
使⽤如下C#程序语⾔:int MaxNum = 3;for (int monk = MaxNum; monk >= 0; monk--){for (int savage = MaxNum; savage >= 0; savage--){Console.Write("{{" + monk + "," + savage + "},{" + (MaxNum - monk) + "," + (MaxNum - savage) + "}} ");}Console.Write("\n");}⽣成16种状态图↓↓↓↓↓↓↓↓↓↓↓状态图含义:{a,b}:a,左岸修道⼠数量;b,左岸野⼈数量。
--------仅考虑左岸传教⼠和野蛮⼈数量(所有状态图)------------------------{3,3} {3,2} {3,1} {3,0}{2,3} {2,2} {2,1} {2,0}{1,3} {1,2} {1,1} {1,0}{0,3} {0,2} {0,1} {0,0}其中{3,3}是起始状态图;{0,0}是终⽌状态图。
人工智能试题及答案

人工智能试题及答案【篇一:人工智能经典试题及答案】ass=txt>2.8 设有如下语句,请用相应的谓词公式分别把他们表示出来:s(1) 有的人喜欢梅花,有的人喜欢菊花,有的人既喜欢梅花又喜欢菊花。
解:定义谓词dp(x):x是人l(x,y):x喜欢y其中,y的个体域是{梅花,菊花}。
将知识用谓词表示为:(?x )(p(x)→l(x, 梅花)∨l(x, 菊花)∨l(x, 梅花)∧l(x, 菊花))(2) 有人每天下午都去打篮球。
解:定义谓词p(x):x是人b(x):x打篮球a(y):y是下午将知识用谓词表示为:a(?x )(?y) (a(y)→b(x)∧p(x))(3) 新型计算机速度又快,存储容量又大。
解:定义谓词nc(x):x是新型计算机f(x):x速度快b(x):x容量大将知识用谓词表示为:(?x) (nc(x)→f(x)∧b(x))(4) 不是每个计算机系的学生都喜欢在计算机上编程序。
解:定义谓词s(x):x是计算机系学生l(x, pragramming):x喜欢编程序u(x,computer):x使用计算机将知识用谓词表示为:? (?x) (s(x)→l(x, pragramming)∧u(x,computer))(5) 凡是喜欢编程序的人都喜欢计算机。
解:定义谓词p(x):x是人l(x, y):x喜欢y将知识用谓词表示为:(?x) (p(x)∧l(x,pragramming)→l(x, computer))2.9 用谓词表示法求解机器人摞积木问题。
设机器人有一只机械手,要处理的世界有一张桌子,桌上可堆放若干相同的方积木块。
机械手有4个操作积木的典型动作:从桌上拣起一块积木;将手中的积木放到桌之上;在积木上再摞上一块积木;从积木上面拣起一块积木。
积木世界的布局如下图所示。
图机器人摞积木问题解:(1) 先定义描述状态的谓词clear(x):积木x上面是空的。
(x, y):积木x在积木y的上面。
传教士野人问题参考答案

传教士野人问题参考答案传教士-野人问题有N个传教士和N个野人要过河,现在有一条船只能承载K个人(包括野人),K<N,在任何时刻,如果有野人和传教士在一起,必须要求传教士的人数多于或等于野人的人数。
设M为传教士的人数,C为野人的人数,用状态空间发求解此问题的过程如下:M、C = N,boat = k,要求M>=C且M+C <= K初始状态目标状态L R L RM 3 0 M 0 3C 3 0 C 0 3B 1 0 B 0 1(1)用三元组来表示(ML , CL , BL)其中0<=ML , CL <= 3 , BL ∈{ 0 , 1}(3 , 3 , 1) (0 , 0 , 0)(2)规则集合P10if ( ML ,CL , BL=1 ) then ( ML–1 , CL , BL –1 )P01if ( ML ,CL , BL=1 ) then ( ML , CL–1 , BL –1 )P11if ( ML ,CL , BL=1 ) then ( ML–1 , CL–1 , BL –1 )P20if ( ML ,CL , BL=1 ) then ( ML–2 , CL , BL –1 )P02if ( ML ,CL , BL=1 ) then ( ML , CL–2 , BL –1 )Q10if ( ML ,CL , BL=0 ) then ( ML+1 , CL , BL+1 )Q01if ( ML ,CL , BL=0 ) then ( ML , CL+1 , BL +1 )Q11if ( ML ,CL , BL=0 ) then ( ML+1 , CL +1, BL +1 )Q20 if ( ML ,CL , BL=0 ) then ( ML+2 , CL +2, BL +1 )Q02if ( ML ,CL , BL=0 ) then ( ML , CL +2, BL +1 )(3)寻找一个启发式函数引导规则的选用右岸总人数6 – ML – CL 两岸中传教士数目>=野人数目f =–∞其它f=3 f=2 f=1 f=1f=1 f=2 (3,3,(3,(2,(3,(3,2,(3,0,f=3 (3,1,f=2 (1,1,f=4 (2,2,f=2 (1,1,f=4 (2,2,f=2 (0,2,f=4 (0,3,f=3 (0,1,f=5(0,2,f=4 (0,0,f=3 (1,1,f=46.2.3 用状态空间法求解传教士和食人者问题例6-2 传教士和食人者问题(The Missionaries and Cannibals Problem)。
人工智能:野人与修道士问题

野人与修道士问题(Missionaries-and-Cannibals Problem )[修道士与野人问题]:三个野人与三个传教士来到河边,打算乘一只船从右岸渡到左岸去,该船的最大负载能力为两个人。
在任何时候,如果野人人数超过传教士人数,那么野人就会把传教士吃掉。
用状态空间法表示修道士与野人问题并设计编写计算机程序求问题的解。
问题分析:从上图可知,修道士、野人和船一共有六种可能,M L 、C L 、B L 、M R 、C R 、B R 。
可以表示为q =(M ,C ,B ),其中m 表示修道士的数目(0、1、2、3)、c 表示野人的数目(0、1、2、3)、b 表示船在左岸(1)或右岸(0)。
1、定义状态的描述形式:(m ,c ,b )2、表示所有可能的状态,并确定初始状态集和目标状态集:s0(3,3,1) s8(1,3,1) s16(3,3,0) s24(1,3,0)s1(3,2,1) s9(1,2,1) s17(3,2,0) s25(1,2,0)s2(3,1,1) s10(1,1,1) s18(3,1,0) s26(1,1,0)s3(3,0,1) s11(1,0,1) s19(3,0,0) s27(1,0,0)s4(2,3,1) s12(0,3,1) s20(2,3,0) s28(0,3,0)s5(2,2,1) s13(0,2,1) s21(2,2,0) s29(0,2,0)s6(2,1,1) s14(0,1,1) s22(2,1,0) s30(0,1,0)s7(2,0,1) s15(0,0,1) s23(2,0,0) s31(0,0,0)初始状态:(3,3,1)目标状态:(0,0,0)3、定义算符:L ij :把i 个修道士,j 个野人从河的左岸送到右岸R ij :把i 个修道士,j 个野人从河的右岸送到左岸整个问题就抽象成了怎样从初始状态经中间的一系列状态达到目标状态。
问修道士M野 人C 左L 右R题状态的改变是通过划船渡河来引发的,所以合理的渡河操作就成了通常所说的算符,根据题目要求,可以得出以下5个算符(按照渡船方向的不同,也可以理解为10个算符):渡1野人、渡1牧师、渡1野人1牧师、渡2野人、渡2牧师即:L01或R01,L10或R10,L11或R11,L02或R02,L20或R204、状态空间图:5、设计编写计算机程序求问题的解:算法:在应用状态空间表示和搜索方法时,用(M,C,B)来表示状态描述,其中M和C分别表示在左岸的传教士与野人数。
数据结构修道士野人渡河问题c语言代码

数据结构修道士野人渡河问题c语言代码以下是一个使用C语言解决修道士野人渡河问题的代码示例: ```c#include <stdio.h>#include <stdbool.h>#define MAX 100int num_of_missionaries;int num_of_cannibals;int num_of_boats;int left_missionaries;int left_cannibals;int right_missionaries;int right_cannibals;bool left_bank;void initialize() {left_missionaries = num_of_missionaries;left_cannibals = num_of_cannibals;right_missionaries = 0;right_cannibals = 0;left_bank = true;}bool is_valid_state(int missionaries, int cannibals) {if (missionaries < 0 || cannibals < 0) {return false;}if (missionaries > 0 && cannibals > missionaries) {return false;}if (left_bank) {int new_left_missionaries = left_missionaries - missionaries;int new_left_cannibals = left_cannibals - cannibals;int new_right_missionaries = right_missionaries + missionaries;int new_right_cannibals = right_cannibals + cannibals; if ((new_left_missionaries >= 0 && new_left_cannibals >= 0) ||(new_right_missionaries >= 0 && new_right_cannibals >= 0)) {if ((new_left_missionaries == 0 ||new_left_missionaries >= new_left_cannibals) &&(new_right_missionaries == 0 || new_right_missionaries >= new_right_cannibals)) {return true;}}}else {int new_left_missionaries = left_missionaries + missionaries;int new_left_cannibals = left_cannibals + cannibals;int new_right_missionaries = right_missionaries - missionaries;int new_right_cannibals = right_cannibals - cannibals; if ((new_left_missionaries >= 0 && new_left_cannibals >= 0) ||(new_right_missionaries >= 0 && new_right_cannibals >= 0)) {if ((new_left_missionaries == 0 ||new_left_missionaries >= new_left_cannibals) &&(new_right_missionaries == 0 || new_right_missionaries >= new_right_cannibals)) {return true;}}}return false;}void move(int missionaries, int cannibals) {if (left_bank) {left_missionaries -= missionaries;left_cannibals -= cannibals;right_missionaries += missionaries;right_cannibals += cannibals;}else {left_missionaries += missionaries;left_cannibals += cannibals;right_missionaries -= missionaries;right_cannibals -= cannibals;}left_bank = !left_bank;}void print_state() {printf("Left Bank: %d missionaries, %d cannibals" " left_missionaries, left_cannibals);printf("Right Bank: %d missionaries, %d cannibals" "" right_missionaries, right_cannibals);}void solve(int missionaries, int cannibals, int depth) { if (missionaries == 0 && cannibals == 0) {print_state();return;}for (int i = 0; i <= num_of_boats; i++) {if (is_valid_state(missionaries, cannibals) &&is_valid_state(i, num_of_boats - i)) {move(missionaries, cannibals);solve(missionaries - i, cannibals - (num_of_boats - i), depth + 1);move(-missionaries, -cannibals);}}}int main() {printf("Enter the number of missionaries: " scanf("d" &num_of_missionaries);printf("Enter the number of cannibals: " scanf("d"&num_of_cannibals);printf("Enter the number of boats: " scanf("d"&num_of_boats);initialize();printf("nSolution:""n"" solve(num_of_missionaries, num_of_cannibals, 0);return 0;}```这个代码使用了回溯法来解决修道士野人渡河问题。
A星传教士和野人问题

A*传教士和野人问题(2008-06-27 10:53:16)转载▼问题描述设有3个传教士和3个野人来到河边,打算乘一只船从左岸渡到右岸去。
该船的负载能力为两人。
在任何时候,如果野人人数超过传教士人数,那么野人就会把传教士吃掉。
他们怎样才能用这条船安全地把所有人都渡河过去?问题表示:需要考虑两岸的修道士人数和野人人数,船的位置。
用三元式表示状态:S= (m, n, B)其中,m表示左岸修道士人数,n表示左岸野人人数,B表示左岸船的数目。
评估函数的建立。
评估函数为f=d+h=d+M+N-2*BM表示左岸的传教士的人数,N表示左岸野人的数目,B取值为0或1 。
1表示船在左岸,0 表示船在右岸。
d 表示节点的深度。
下面我们来证明h(n)=M+C-2B是满足A*条件的。
我们分两种情况考虑。
先考虑船在左岸的情况。
如果不考虑限制条件,也就是说,船一次可以将三人从左岸运到右岸,然后再有一个人将船送回来。
这样,船一个来回可以运过河2人,而船仍然在左岸。
而最后剩下的三个人,则可以一次将他们全部从左岸运到右岸。
所以,在不考虑限制条件的情况下,也至少需要摆渡[(M+N-3)/2]*2+1次。
其中分子上的"-3"表示剩下三个留待最后一次运过去。
除以"2"是因为一个来回可以运过去2人,需要[(M+N-3)/2]个来回,而"来回"数不能是小数,需要向上取整,这个用符号[ ]表示。
而乘以"2"是因为一个来回相当于两次摆渡,所以要乘以2。
而最后的"+1",则表示将剩下的3个运过去,需要一次摆渡。
化简有:M+N-2。
再考虑船在右岸的情况。
同样不考虑限制条件。
船在右岸,需要一个人将船运到左岸。
因此对于状态(M,N,0)来说,其所需要的最少摆渡数,相当于船在左岸时状态(M+1,N,1)或(M,N+1,1)所需要的最少摆渡数,再加上第一次将船从右岸送到左岸的一次摆渡数。
(北邮)人工智能期中测验

2006年秋季人工智能小测验(1)
1.为什么要学习人工智能,谈谈你的理解?
2.用语义网络表示下列知识:
所有的鸽子都是鸟。
所有的鸽子都有翅膀。
信鸽是一种鸽子,它有翅膀,能识途。
3.设已知:
(1)能阅读的人是识字的。
(2)海豚不识字。
(3)有些海豚是很聪明的。
用输入归结策略证明:有些很聪明的人并不识字。
4.修道士和野人问题。
设有3个修道士和3个野人来到河边,打算
用一条船从河的左岸渡到河的右岸去。
但该船每次只能装载两个人,在任何岸边野人的数目都不得超过修道士的人数,、否则修道士就会被野人吃掉。
假设野人服从任何一种过河安排,请使用状态空间搜索法,规划一使全部6人安全过河的方案。
(提示:应用状态空间表示和搜索方法时,可用(Nm,Nc)来表示状态描述,其中Nm和Nc分别为传教士和野人的人数。
初始状态为(3,3),而可能的中间状态为(0,1),(0,2),(0,3),(1,1),(2,1),(2,
2),(3,0),(3,1),(3,2)等。
)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
修道士与野人问题6(修道士与野人问题这是一个古典问题。
假设有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数(除非修道士个数为0)。
如果两种人都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
要求:(1)用一个三元组(x1,x2,x3)表示渡河过程中各个状态。
其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
例如(2,1,1)表示起始岸上有两个修道士,一个野人,小船在起始岸一边。
采用邻接表做为存储结构,将各种状态之间的迁移图保存下来。
(2)采用广度搜索法,得到首先搜索到的边数最少的一条通路。
(3)输出数据若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态?…中间状态?…初始状态。
若问题无解,则给出“渡河失败”的信息。
(4)求出所有的解。
1(需求分析有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数,否则修道士就会有危险,设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
用三元组(x1,x2,x3)来表示渡河过程中各个状态,其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态?…中间状态?…初始状态,若问题无解,则给出“渡河失败”的信息。
2(设计2.1 设计思想(1)数据结构设计逻辑结构设计: 图型结构存储结构设计: 链式存储结构采用这种数据结构的好处:便于采用广度搜索法,得到首先搜索到的边数最少的一条通路,输出一个最佳方案,采用图的邻接表存储结构搜索效率较高。
(2)算法设计算法设计的总体设计思路为:在得到修道士人数和小船的容纳人数后,用boatcase得到所有情况,然后再进行安全性检查,以减去修道士少于野人的情况,接着用孩子兄弟结点表示法,将去对面的路作为孩子结点,路与路是兄弟关系,到达另一边时,同样以这种方法,直到找到(0,0,0)。
主要分为4个模块:boatcase生成所有情况,BFS得到边数最少的最佳方案,safe安全性检测,print输出安全渡河的全过程。
各个模块要完成的主要功能分别为:生成模块:生成所有的可能渡河情况安全检测模块:对所有的可能渡河情况进行安全检测,,以减去修道士少于野人的情况广度搜索模块:采用广度搜索法,得到首先搜索到的边数最少的一条通路输出模块:输出所有安全渡河的全过程主程序的流程图:建立邻接表调用函数 Linkinit( )来进行初始化把初始状态插调用函数Insertson( )来插入结点入邻接表中进行广搜找到成调用函数guangdu( )功的方案打印输出各种方调用函数print( ) 案2.2 设计表示(1)函数调用关系图guangdu boatcase safe insertson printinsertbro(2)函数接口规格说明void Linkinit(Link **head)void insertson(Link *head, DataType x) void insertbro(Link*head,DataType x) int boatcase(DataType x,int n) void guangdu(Link *p,int n,int c) int safe(DataType x,int n)void print(Link *q,Link *p)2.3 详细设计, 生成模块int boatcase(DataType x,int n) {int i=0,a,b,t=0;if(x.cw) {a=0;b=n-a; while (a+b>=1) {t++;while (b>=0) {array[i].xds=a; array[i].yr=b; i++;a++;b--;}a=0; b=n-a-t; }}else{a=1;b=0;t=0; while (a+b<=n) {t++;while (a>=0) {array[i].xds=a*(-1);array[i].yr=b*(-1);i++;a--;b++;}a=array[0].xds*(-1)+t;b=0;}}return i;}, 安全检测模块int safe(DataType x,int n){if((x.xds>=x.yr||x.xds==0)&&((n-x.xds)>=(n-x.yr)||x.xds==n)&&x.xds>=0&&x.xds<=n&& x.yr>=0&&x.yr<=n)return 1;elsereturn 0;}, 广度搜索模块void guangdu(Link *p,int n,int c) {Link *q,*t;DataType tem;int i,flag1,flag2,g=0,j,count=0;q=p->son;while (q!=NULL)/{flag1=0;j=boatcase(q->data,c);for (i=0;i<j;i++) {tem.xds=q->data.xds-array[i].xds;tem.yr=q->data.yr-array[i].yr;tem.cw=1-q->data.cw;t=q;if (safe(tem,n)) {flag2=1;//1while (t!=p){if(tem.xds== t->data.xds&&tem.yr==t->data.yr&&tem.cw==t->data.cw) {flag2=0;break;}t=t->par;}if(flag2==1){if (flag1==0){insertson(q, tem);flag1=1;}elseinsertbro(q,tem);if (tem.xds==0&&tem.yr==0&&tem.cw==0) {print(q,p);count++;}}}}q=q->next;}if (count==0)printf("无法成功渡河!\n");elseprintf("有%d种渡河方式。
\n",count); }, 输出模块void print(Link *q,Link *p) {DataType a[100];int i=1;a[0].cw=0;a[0].xds=0;a[0].yr=0;while (q!=p){a[i++]=q->data;q=q->par;}while ((--i)>-1){printf("( %d %d %d )",a[i].xds,a[i].yr,a[i].cw);if (!(a[i].xds==0&&a[i].yr==0&&a[i].cw==0)){if(a[i].cw==1)printf("-->(%d %d)-->(%d %d 0)\n",a[i].xds-a[i-1].xds,a[i].yr-a[i-1].yr,a[i-1].xds,a[i-1].yr);elseprintf(" <-- ( %d %d ) <-- ( %d %d 1 )\n",(a[i].xds-a[i-1].xds)*(-1),(-1)*(a[i].yr-a[i-1].yr),a[i-1].xds,a[i-1].yr);}}printf("渡河成功!\n");}3(调试分析(1)本题是采用邻接表做为存储结构,将各种状态之间的迁移图保存下来,并用孩子兄弟表示法,以实现广度搜索;刚编好程序时出现死循环的现象,例如:带过去2个野人又带回来2个野人,在和其他同学讨论后,采用了2个标志位来避免出现死循环的现象,在进行运行的时候,曾出现了打印输出错误,经过一步一步调试,发现在插入结点的时候出现了插入错误,即没有考虑到pre的改变,通过改正,重新运行检测,运行结果正确,在排版时通过一步步调试,参考了课本和老师的课件,并与和其他同学讨论后,终于通过调试和改正,,能够使输出结果很明显的渡河方案。
(2)可改进内容:显示表示哪些是渡河次数最短的,最佳渡河方案一共有几种,并输出每种最佳渡河方案,另外,可尝试用深度优先搜索算法来找最佳方案。
4(用户手册本程序在VC++6.0环境下运行,根据提示输入相应的渡河人数和小船能容纳的人数即可。
5(测试数据及测试结果测试用例1测试输入: n=3,c=2测试目的: 检验程序运行时是否会陷入死循环正确输出: 见截屏1,2实际输出: 见截屏 3,4错误原因: 未正确设置标志位,出现死循环现象当前状态: 已改正截屏1截屏2截屏3截屏46(源程序清单#include <stdio.h> #include <malloc.h> #include <stdlib.h> typedef struct{int xds; //修道士个数int yr; //野人个数int cw; //船的位置}DataType;DataType array[50000]; typedef struct node//结构体定义{DataType data;struct node *son;//儿子struct node *bro;//兄弟struct node *par;//双亲struct node *next; }Link;void Linkinit(Link **head) //初始化操作 {*head=(Link *)malloc(sizeof (Link)); //申请动态空间(*head)->son=NULL;(*head)->bro=NULL;(*head)->par=NULL;(*head)->next=NULL; }void insertson(Link *head, DataType x) //在邻接表中插入儿子结点的操作{Link *q,*s;q=(Link *)malloc(sizeof (Link));q->data=x;head->son=q;//将x插入给头结点的儿子指针s=head;while (s->next!=NULL)s=s->next;q->par=head;q->son=NULL;q->bro=NULL;s->next=q;q->next=NULL;}void insertbro(Link *head,DataType x)//在邻接表中插入兄弟结点的操作,//所有的兄弟结点都指向他们右边的结点{Link *q,*s;q=(Link *)malloc(sizeof (Link));s=head->son;q->data=x;while (s->bro!=NULL)s=s->bro;s->bro=q;s->next=q;q->next=NULL;q->bro=NULL;q->par=head;q->son=NULL;}int boatcase(DataType x,int n) //生成所有情况; { int i=0,a,b,t=0;if(x.cw) //在此岸,上船的人多优先{a=0;b=n-a; //a为修道士b为野人while (a+b>=1)//当船上有人时{t++;while (b>=0)//当野人个数不为负数{array[i].xds=a;array[i].yr=b;i++;a++;b--;}a=0;//船上空位个数b=n-a-t;}}else//在对岸,上船的人少优先{a=1;b=0;t=0;while (a+b<=n){t++;//船上的人数while (a>=0){array[i].xds=a*(-1);array[i].yr=b*(-1);i++;a--;b++;}a=array[0].xds*(-1)+t;b=0;}}return i; //i为总数量}int safe(DataType x,int n)//安全性检测{ // 起始目的if((x.xds>=x.yr||x.xds==0)&&((n-x.xds)>=(n-x.yr)||x.xds==n)&&x.xds>=0&&x.xds<=n&& x.yr>=0&&x.yr<=n)return 1;//船上修道士elsereturn 0;}void print(Link *q,Link *p) //打印安全渡河的过程,当船到对岸时,把对岸当作其始岸,此岸当作彼岸{DataType a[100];int i=1;a[0].cw=0;a[0].xds=0;a[0].yr=0;while (q!=p)//避免出现相同情况而循环{a[i++]=q->data;//将一次过河的情况给b[i]q=q->par;}while ((--i)>-1) //输出过河图{printf("( %d %d %d )",a[i].xds,a[i].yr,a[i].cw);if (!(a[i].xds==0&&a[i].yr==0&&a[i].cw==0)){if(a[i].cw==1)printf("-->(%d %d)-->(%d %d0)\n",a[i].xds-a[i-1].xds,a[i].yr-a[i-1].yr,a[i-1].xds,a[i-1].yr);//a[i].xds-a[i-1].xds表示过河过程中船上的修道士数,a[i].yr-a[i-1].yr表示过河过程中船上的野人数elseprintf(" <-- ( %d %d ) <-- ( %d %d 1 )\n",(a[i].xds-a[i-1].xds)*(-1),(-1)*(a[i].yr-a[i-1].yr),a[i-1].xds,a[i-1].yr);}}printf("渡河成功!\n");}void guangdu(Link *p,int n,int c)//广度搜索{Link *q,*t;DataType tem;int i,flag1,flag2,g=0,j,count=0;q=p->son;while (q!=NULL)//逐个搜索儿子结点{flag1=0;//等于0表示插入儿子结点,1表示插入兄弟结点j=boatcase(q->data,c);//可能过河的情况for (i=0;i<j;i++)//搜索兄弟结点{tem.xds=q->data.xds-array[i].xds;tem.yr=q->data.yr-array[i].yr;tem.cw=1-q->data.cw;t=q;if (safe(tem,n))//是否安全{flag2=1;//1表示没有死循环while (t!=p)//保证不会出现循环{if(tem.xds==t->data.xds&&tem.yr==t->data.yr&&tem.cw==t->data.cw) {//出现相当情况时候flag2=0;break;}t=t->par;}if(flag2==1){if (flag1==0)//插入儿子结点{insertson(q, tem);flag1=1;}else//插入兄弟结点insertbro(q,tem);if (tem.xds==0&&tem.yr==0&&tem.cw==0) {print(q,p);count++;}}}}q=q->next;}if (count==0)printf("无法成功渡河!\n");elseprintf("有%d种渡河方式。