转自:http://blog.csdn.net/orbit/article/details/7563220

题目描述:农夫需要把狼、羊、菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊。请考虑一种方法,让农夫能够安全地安排这些东西和他自己过河。

这个题目考察人的快速逻辑运算和短期记忆力。分析一下,在狼-》羊-》菜这个食物链条中,“羊”处在关键位置,解决问题的指导思想就是将“羊”与“狼”和“菜”始终处于隔离状态,也就是说“羊”应该总是最后被带过河的。来看一个答案:

农夫带羊过河

农夫返回

农夫带狼过河

农夫带羊返回

农夫带菜过河

农夫返回

农夫带羊过河

<结束>

再看一个答案:

农夫带羊过河

农夫返回

农夫带菜过河

农夫带羊返回

农夫带狼过河

农夫返回

农夫带羊过河

<结束>

解决问题都是围绕着羊进行的。

上面已经提到了两个答案,那么,这个问题到底有多少中答案呢?答案还需要用计算机进行穷举。用计算机解决这个问题的关键还是状态遍历,这个和《算法系列――三个水桶均分水问题》一文中提到的状态遍历是一个道理,归根到底就是一个有限状态机。农夫、狼、羊和菜根据它们的位置关系可以有很多个状态,但总的状态数还是有限的,我们的算法就是在这些有限个状态之间遍历,直到找到一条从初始状态转换到终止状态的“路径”,并且根据题目的要求,这条“路径”上的每一个状态都应该是合法的状态。

本题无论是状态建模还是状态转换算法,都比“用三个水桶均分8升水”要简单。首先是状态,农夫、狼、羊和菜做为四个独立的Item,它们的状态都很简单,要么是过河,要么是没有过河,任意时刻每个Item的状态只有一种。如果用“HERE”表示没有过河,用“THERE”表示已经过河,用[农夫,狼,羊,菜]四元组表示某个时刻的状态,则本题的状态空间就是以[HERE,HERE,HERE,HERE]为根的一棵状态树,当这个状态树的某个叶子节点是状态[THERE,THERE,THERE,THERE],则表示从根到这个叶子节点之间的状态序列就是本问题的一个解。

本题的状态转换算法依然是对状态空间中所有状态进行深度优先搜索,因为狼、羊和菜不会划船,所以状态转换算法也很简单,不需要象“用三个水桶均分8升水”问题那样要用排列组合的方式确定转换方法(倒水动作),本题一共只有8种固定的状态转换运算(过河动作),分别是:

农夫单独过河;

农夫带狼过河;

农夫带羊过河;

农夫带菜过河;

农夫单独返回;

农夫带狼返回;

农夫带羊返回;

农夫带菜返回;

本题的广度搜索边界就是这8个动作,依次对这8个动作进行遍历最多可以转换为8个新状态,每个新状态又最多可以转化为8个新新状态,就形成了每个状态节点有8个(最多8个)子节点的状态树(八叉树)。本题算法的核心就是对这个状态树进行深度优先遍历,当某个状态满足结束状态时就输出一组结果。

需要注意的是,并不是每个动作都可以得到一个新状态,比如“农夫带狼过河”这个动作,对于那些狼已经在河对岸的状态就是无效的,无法得到新状态,因此这个八叉树并不是满树。除此之外,题目要求的合法性判断也可以砍掉很多无效的状态。最后一点需要注意的是,即使是有效的状态,也会有重复,在一次深度遍历的过程中如果出现重复的状态可能会导致无穷死循环,因此要对重复出现的状态进行“剪枝”。

程序实现首先要描述状态模型,本算法的状态定义为:

33 struct ItemState

34 {

35   ......

43   State  farmer,wolf,sheep,vegetable;

44   Action curAction;

35   ......

45 };

算法在穷举的过程中需要保存当前搜索路径上的所有合法状态,考虑到是深度优先算法,用Stack是最佳选择,但是Stack没有提供线性遍历的接口,在输出结果和判断是否有重复状态时都需要线性遍历保存的状态路径,所以本算法不用Stack,而是用Deque(双端队列)。

整个算法的核心就是ProcessState()函数,ProcessState()函数通过对自身的递归调用实现对状态树的遍历,代码如下:

291 void ProcessState(deque<ItemState>& states)

292 {

293     ItemState current = states.back(); /*每次都从当前状态开始*/

294     if(current.IsFinalState())

295     {

296         PrintResult(states);

297         return;

298     }

299

300     ItemState next;

301     for(int i = 0; i < action_count; ++i)

302     {

303         if(actMap[i].processFunc(current, next))

304         {

305             if(IsCurrentStateValid(next) && !IsProcessedState(states, next))

306             {

307               states.push_back(next);

308               ProcessState(states);

309               states.pop_back();

310             }

311         }

312     }

313 }

参数states是当前搜索的状态路径上的所有状态列表,所以ProcessState()函数首先判断这个状态列表的最后一个状态是不是最终状态,如果是则说明这个搜索路径可以得到一个解,于是调用PrintResult()函数打印结果,随后的return表示终止设个搜索路径上的搜索。如果还没有达到最终状态,则依次从8个固定的过河动作得到新的状态,并从新的状态继续搜索。为了避免长长的switch…case语句,程序算法使用了表驱动的方法,将8个固定过河动作的处理函数放在一张映射表中,用简单的查表代替switch…case语句。映射表内容如下:

279 ActionProcess actMap[action_count] =

280 {

281     { FARMER_GO,                  ProcessFarmerGo                },

282     { FARMER_GO_TAKE_WOLF,        ProcessFarmerGoTakeWolf        },

283     { FARMER_GO_TAKE_SHEEP,       ProcessFarmerGoTakeSheep       },

284     { FARMER_GO_TAKE_VEGETABLE,   ProcessFarmerGoTakeVegetable   },

285     { FARMER_BACK,                ProcessFarmerBack              },

286     { FARMER_BACK_TAKE_WOLF,      ProcessFarmerBackTakeWolf      },

287     { FARMER_BACK_TAKE_SHEEP,     ProcessFarmerBackTakeSheep     },

288     { FARMER_BACK_TAKE_VEGETABLE, ProcessFarmerBackTakeVegetable }

289 };

表中的处理函数非常简单,就是根据当前状态以及过河动作,得到一个新状态,如果过河动作与当前状态矛盾,则返回失败,以FARMER_GO_TAKE_WOLF动作对应的处理函数ProcessFarmerGoTakeWolf()为例,看看ProcessFarmerGoTakeWolf()函数的代码:

182 bool ProcessFarmerGoTakeWolf(const ItemState& current, ItemState& next)

183 {

184     if((current.farmer != HERE) || (current.wolf != HERE))

185         return false;

186

187     next = current;

188

189     next.farmer    = THERE;

190     next.wolf      = THERE;

191     next.curAction = FARMER_GO_TAKE_WOLF;

192

193     return true;

194 }

当过河动作对应的处理函数返回成功,表示可以得到一个不矛盾的新状态时,就要对新状态进行合法性检查,首先是检查是否满足题目要求,比如狼和羊不能独处以及羊和菜不能独处,等等,这个检查在IsCurrentStateValid()函数中完成。接着是检查新状态是否和状态路径上已经处理过的状态有重复,这个检查由IsProcessedState()函数完成,IsProcessedState()函数的实现也很简单,就是遍历states,与新状态比较是否有相同状态,代码如下:

131 bool IsProcessedState(deque<ItemState>& states, ItemState& newState)

132 {

133     deque<ItemState>::iterator it = find_if( states.begin(), states.end(),

134                                              bind2nd(ptr_fun(IsSameItemState), newState) );

135

136     return (it != states.end());

137 }

运行程序,最终得到的结果是:

Find Result 1:

Unknown action, item states is : 0 0 0 0

Farmer take sheep go over river, item states is : 1 0 1 0

Farmer go back, item states is : 0 0 1 0

Farmer take wolf go over river, item states is : 1 1 1 0

Farmer take sheep go back, item states is : 0 1 0 0

Farmer take vegetable go over river, item states is : 1 1 0 1

Farmer go back, item states is : 0 1 0 1

Farmer take sheep go over river, item states is : 1 1 1 1

Find Result 2:

Unknown action, item states is : 0 0 0 0

Farmer take sheep go over river, item states is : 1 0 1 0

Farmer go back, item states is : 0 0 1 0

Farmer take vegetable go over river, item states is : 1 0 1 1

Farmer take sheep go back, item states is : 0 0 0 1

Farmer take wolf go over river, item states is : 1 1 0 1

Farmer go back, item states is : 0 1 0 1

Farmer take sheep go over river, item states is : 1 1 1 1

看来确实是只有两种结果

农夫、狼、羊过河问题相关推荐

  1. 【华为OD机试真题】羊、狼、农夫过河(狼羊过河)(c++javapython)

    狼羊过河 时间限制:1s空间限制:256MB限定语言:不限 题目描述: 一农夫带着m只羊,n只狼过河,农夫有一条可载x只狼/羊的船:农夫在时或者羊的 数量大于狼时,狼不会攻击羊:农夫在不损失羊的情况下 ...

  2. DLS 深度受限搜索 狼羊 过河 问题 python 实现

    深度受限搜索(DLS)简单地说就是深度有限搜索(DFS)+深度限制(limit) DLS伪代码 实例:狼羊 过河 问题 3只羊和3头狼在河岸A,想要过河抵达河岸B.它们只有一艘船并且船上必须有1-2只 ...

  3. 农夫 狼 羊 白菜 java,一个农夫带着一头狼,一头羊和白菜过桥,一次只能带一样过桥,狼会吃羊,年会吃白菜,请问农夫该怎么办?...

    一个农夫带着一头狼,一头羊和白菜过桥,一次只能带一样过桥,狼会吃羊,年会吃白菜,请问农夫该怎么办?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让 ...

  4. FZU(2188):狼羊过河问题,状态转移,BFS求解

    题目连接:http://acm.fzu.edu.cn/problem.php?pid=2188 意思:中文题目,就是给定狼羊的数目,求最小运输量把狼和羊全部送到对岸,条件是,岸上和船上的羊不能少于狼的 ...

  5. 农夫狼羊白菜过河C语言程序,农夫过河(带羊,菜,狼,过河) C语言程序设计流程图...

    #include #include #include #define MAX_STEP 20 //index: 0 - 狼,1-羊,2-菜,3-农夫,value:0-本岸,1-对岸 int a[MAX ...

  6. 农夫 狼 羊 白菜 java,农夫、狼、羊、白菜(回溯法求解)

    为了复试准备,最近做了很多上机题,映象最最深的就是一道关于农夫.狼.羊.白菜的问题.该问题描述如下:有一个农夫带一只羊.一筐菜和一只狼过河.果没有农夫看管,则狼要吃羊,羊要吃菜.但是船很小,只够农夫带 ...

  7. 用C语言解决狼羊白菜过河的思路,基于visual Studio2013解决C语言竞赛题之1079狼羊过河...

        题目 解决代码及点评 /************************************************************************/ /* ...

  8. c语言编程人狼羊菜过河,基于visual Studio2013解决C语言竞赛题之1079狼羊过河

        题目 解决代码及点评 /************************************************************************/ /* ...

  9. java狼追着羊跑的程序_Java编程能力强化——狼羊过河问题

    题目:有3只狼和3只羊要过河,只有一条船,一次最多只能坐两只动物并且每次必须有动物开船,如果某一边的狼的个数大于羊的个数,羊将被吃掉,编程给出解. 关于编程思路,参考:Java编程能力强化(2)--搜 ...

  10. 狼羊菜过河问题c语言算法,算法系列之十四:狼、羊、菜和农夫过河问题

    题目描述:农夫需要把狼.羊.菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊.请考虑一种方法,让农夫能够 ...

最新文章

  1. SyntaxError: EOL while scanning string literal
  2. Vmware虚拟机的复制后无法使用的问题和解决
  3. SD-WAN为企业业务出海提供网络保障
  4. Oracle查询所有表结构和表名称及备注
  5. mysql 查看端口_新手连接MySQL数据库,再也不怕连不上了
  6. sqlmap安装_黑客神器系列篇章之“sqlmapquot;
  7. Python3 爬虫学习笔记 C15【代理的基本使用】
  8. linux find 用法详解 + 实例
  9. 数据仓库入门(实验6)添加层次结构
  10. centos 6.8 配置csh的shell和环境变量
  11. 轻松自动化---selenium-webdriver(python) (十一)
  12. 【原创】VBA学习笔记(21) VBA函数,appliacation函数,工作表函数,三种同名函数对比举例(3个例子)
  13. 树莓派Ubuntu18.04使用及联网
  14. java一元二次方程用if_用javascript写一个求一元二次方程的页面 用JAVA写一个求解一元二次方程的类...
  15. 360Lib整体介绍
  16. Jupyter notebook 打开网页无法显示
  17. Sourcery G++ Lite 一个arm开发工具
  18. 实验五—JEPG 原理分析及 JPEG 解码器的调试
  19. for in遍历对象时break,continue,return尝试
  20. windows多进程探测存活主机

热门文章

  1. QUANT[2]:量化交易策略基本框架搭建
  2. 163企业邮箱价格费用标准是多少?
  3. Python 测试框架pytest
  4. Dapper 下划线
  5. 九州集团“刷脸支付红利风口”交流会议将9月9日举行
  6. Matlab学习笔记2011-09-16【数据分析】
  7. 8-设计模式之行为型模式二(状态模式、观察者模式、中介者模式)
  8. LOWKEY: LEVERAGING ADVERSARIAL ATTACKS TO PROTECT SOCIAL MEDIA USERS FROM FACIAL RECOGNITION
  9. 新西兰奥克兰发生三车追尾事故 造成一死一伤
  10. 抖音反编译:android反编译之最新版本抖音(v7.7.0)安卓版反编译教程