Algorithm series fourteen: wolves, sheep, vegetables and farmers crossing the river

Title description: The farmer needs to transport wolves, sheep, vegetables and himself to the other side of the river. Only the farmer can row the boat, and the boat is relatively small. In addition to the farmer, only one thing can be transported at a time. There is a thorny problem, that is, if Without the farmer watching, sheep will steal vegetables and wolves will eat sheep. Please consider a way for the farmer to safely arrange these things to cross the river with himself.

 

        This topic examines people's fast logical operations and short-term memory. Analyze, in the food chain of wolf-"sheep-"cai, "sheep" is at a key position, and the guiding ideology for solving the problem is to always isolate "sheep" from "wolf" and "cai", that is to say "Sheep" should always be the last to be taken across the river. Look at an answer:

 

The farmer takes the sheep across the river

The farmer returns

The farmer took the wolf across the river

The farmer returns with the sheep

The farmer brings vegetables across the river

The farmer returns

The farmer takes the sheep across the river

< end >

 

Look at another answer:

The farmer takes the sheep across the river

The farmer returns

The farmer brings vegetables across the river

The farmer returns with the sheep

The farmer took the wolf across the river

The farmer returns

The farmer takes the sheep across the river

< end >

 

Solving problems revolves around sheep.

         Two answers have been mentioned above, so how many answers are there in this question? The answer needs to be exhaustive with a computer. The key to using a computer to solve this problem is state traversal. This is the same as the state traversal mentioned in the article "Algorithm Series-Three Bucket Water Sharing Problem". In the final analysis, it is a finite state machine. Farmers, wolves, sheep and vegetables can have many states according to their positional relationship, but the total number of states is still limited. Our algorithm traverses between these finite states until it finds a transition from the initial state to the end state According to the requirements of the topic, every state on this “path” should be a legal state.

         Whether it is state modeling or state transition algorithm, this question is simpler than "dividing 8 liters of water equally with three buckets ". The first is the status. The farmer, wolf, sheep and vegetable are four independent items . Their status is very simple, either crossing the river or not crossing the river. There is only one status for each item at any time . If you use " HERE " to indicate that you have not crossed the river, use " THERE " to indicate that you have crossed the river, and use the [ farmer, wolf, sheep, dish ] quadruple to indicate the state at a certain moment, then the state space of this question is [HERE , HERE , HERE , HERE] is the root of a state tree, when a leaf node of the state tree is state [THERE , THERE , THERE , THERE] , it means that the state sequence from the root to the leaf node is the problem One solution.

The state transition algorithm for this question is still a depth-first search for all states in the state space. Because wolves, sheep, and vegetables cannot row a boat, the state transition algorithm is also very simple. There is no need for the problem of " dividing 8 liters of water with three buckets " In that case, the conversion method (pouring action) should be determined by permutation and combination. There are only 8 fixed state conversion operations (river crossing actions) in this question , which are:

The farmer crosses the river alone;

The farmer took the wolf across the river;

The farmer takes the sheep across the river;

The farmer brought vegetables across the river;

The farmer returns alone;

农夫带狼返回;

农夫带羊返回;

农夫带菜返回;

 

        本题的广度搜索边界就是这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

 

看来确实是只有两种结果。

 

Guess you like

Origin blog.csdn.net/orbit/article/details/7563220