题目描述:农夫需要把狼、羊、菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊。请考虑一种方法,让农夫能够安全地安排这些东西和他自己过河。
想这个问题一连想了好几天,本人没有系统的学过算法,有些概念也不是很清楚,只因解决问题为目标。
尝试过图论解决,但用floyed算法只能算出最短路径值,如何输出过程,一直没想出好的解决方法。
然后看了下面这篇文章,尝试抛弃图论,用树的思想来解决这个问题。建议阅读下面代码时,先看看这篇文章。
参考资料:
在写代码时,本人采用了上述文章中的思想,又借鉴了图论中存储结点的一些方法。
我觉得这样写应该非常容易看懂了。具体思路见代码。
1 #include <stdio.h>
2 #define INF 9999
3 //8个动作
4 char *action[8]={"农夫单独过河","农夫带狼过河","农夫带羊过河","农夫带菜过河",
5 "农夫单独返回","农夫带狼返回","农夫带羊返回","农夫带菜返回"};
6 //10种状态
7 char *state[10]={"人狼羊菜","人狼羊","人狼菜","人羊菜","人羊","狼菜","狼","羊","菜","空"};
8
9 //状态转换规则:GA[i][j]=k 表示【状态i】可以通过【动作k】转换到【状态j】,GA[i][j]=INF表示不可直接转换
10 int GA[10][10]={INF,INF,INF,INF,INF, 2,INF,INF,INF,INF,
11 INF,INF,INF,INF,INF,INF, 2, 1,INF,INF,
12 INF,INF,INF,INF,INF, 0, 3,INF, 1,INF,
13 INF,INF,INF,INF,INF,INF,INF, 3, 2,INF,
14 INF,INF,INF,INF,INF,INF,INF, 0,INF, 2,
15 6,INF, 4,INF,INF,INF,INF,INF,INF,INF,
16 INF, 6, 7,INF,INF,INF,INF,INF,INF,INF,
17 INF, 5,INF, 7, 4,INF,INF,INF,INF,INF,
18 INF,INF, 5, 6,INF,INF,INF,INF,INF,INF,
19 INF,INF,INF,INF, 6,INF,INF,INF,INF,INF};
20
21 //记录每一步的动作
22 int record_action[20];
23 //记录每一步动作后的状态
24 int record_state[20];
25
26 //搜索从第step步开始、第i个结点到第n个结点的过程(step从0算起)
27 void search(int i,int n,int step)
28 {
29 int k;//动作
30 int j;//可能要转换到的状态
31 if(i==n)
32 {
33 for(k=0;k<step;k++)
34 printf("step %d: %s,左岸还剩 %s\n",k+1,action[record_action[k]],state[record_state[k]]);
35 printf("step count:%d\n\n",step);
36 return;
37 }
38 //查找在当前【状态i】下能转换到的【其它状态j】,并且【状态j】不能在之前出现过
39 //查找时可能会出现多个 j,所以这是一个多叉树
40 for(k=0;k<8;k++)
41 {
42 for(j=0;j<10;j++)
43 if(GA[i][j]!=INF&&GA[i][j]==k)//判断状态i能否通过动作k转换到状态j
44 {
45 int m;
46 //下面这个循环是判断状态j在之前是否出现过
47 for(m=0;m<step;m++)
48 if(j==record_state[m])break;
49 if(m<step)continue;
50 //如果j满足前面所有条件,则记录这一步
51 record_action[step]=k; //第step步所使用的动作
52 record_state[step]=j; //第step步所转换的状态
53 search(j,n,step+1); //继续搜索下一步
54 }
55 }
56
57 }
58 int main()
59 {
60 search(0,9,0);
61 return 0;
62 }