您的位置:首页 > 其它

数学回味系列之11 - 农夫过河问题

2017-02-25 23:07 423 查看
问题提出:

       对象:农夫、狼、羊、白菜

       目标:农夫想划船 把狼、羊、菜和自己运到河对岸,船比较小,农夫每次只能运一种东西过河

       约束条件:如果没有农夫看着,羊会偷吃菜,狼会吃羊

       考虑一种方法,让农夫能够安全地安排过河。

解题思路:

       实际上这是一个状态转换问题,也许做过这个题的童鞋都能给出一个正确答案,比如:

1)带羊过河

2)农夫单独返回

3)带狼过河

4)带羊返回

5)带菜过河

6)单独返回

7)带羊过河

       把这个问题看成一个搜索过程,可以采用两种不同的策略:一种广度优先搜索,另一种深度优先搜索。

深度优先代码:

/*******************************************************************************
* 版权所有 (C) linolzhang 2009
*
* 文件名称:PassRiver.cpp
* 内容说明:
深度优先算法实现过河问题
*******************************************************************************/
#include<iostream>
#include<bitset>
#include<vector>
#include<queue>
#include<stack>

enum Passer{ Farmer, Wolf, Sheep, Cabbage }; // 过河者,共4种对象

bool isSafe(const std::bitset<4> &state)     // 判断状态安全 - state[Passer] 0:未过河 1:已过河
{
if( state[Wolf]==state[Sheep]  && state[Farmer]!=state[Wolf] ||	  // 狼吃羊
state[Sheep]==state[Cabbage] && state[Farmer]!=state[Sheep] ) // 羊吃白菜
return false;
return true;
}

// 求解得到可行性路径
void findRoute(std::vector<int> &vecPath)
{
std::stack<int> discovering; // 待发现的各个状态 - 初始状态为0
discovering.push(0x00);

vecPath[0] = 0; // 初始状态路径初始化
while(!discovering.empty() && vecPath[15] == -1)	// 只要还有下一个状态可以到达,并且尚未到达最终状态
{
std::bitset<4> curState = discovering.top(); discovering.pop(); // 类型转换:int -> bitset<4>
for(int candidate=Farmer; candidate<=Cabbage; candidate++)
{
if(curState[candidate] != curState[Farmer]) // 随农夫过河的必然与农夫在同一侧
continue;

std::bitset<4> nextState = curState; // 农夫过河后的新状态
nextState.flip(Farmer);			     // 农夫必定过河

if(candidate != Farmer) // 如果不是农夫单独过河的情形
nextState.flip(candidate);

int nextIndex = nextState.to_ulong(); // 将状态矢量转换为整型以作为队列元素和路径下标
if( isSafe(nextState) && vecPath[nextIndex] == -1 ) // 如果新状态安全,且尚未被发现,则新状态进入下一状态队列
{
vecPath[nextIndex] = curState.to_ulong(); // 建立当前状态与下一状态的联系
discovering.push(nextIndex);
}
}
}
}

// 显示路由方案
void showRoute(const std::vector<int> &vecPath)
{
if(vecPath[15] == -1) // 如果“1111”状态没有前驱,则没能够成功到达目的状态
return;

std::deque< std::bitset<4> > statePath;	// 将整数表示的路径转换为布尔矢量表示的状态路径,存储状态路径的容器
for(int i=15; i>0; i=vecPath[i])
statePath.push_front(i);
statePath.push_front(0); // 隐式类型转换:int -> bitset<4>

std::bitset<4> current,next,trans; // 当前状态、下一状态、转换状态
bool crossed = true;
int step = 1;
for(unsigned int i=0; i<statePath.size()-1; i++)
{
current = statePath[i];
next = statePath[i+1];
trans = current ^ next; // 状态变量中的变化位

int passer = trans[Wolf] ? Wolf : (trans[Sheep] ? Sheep : (trans[Cabbage] ? Cabbage : -1) ); // 找到第一个变化位
std::cout << "步骤" << step++ << ": ";
if(Wolf == passer)
std::cout << "农夫把狼带" << (crossed ? "到河对岸" : "返回") << std::endl;
else if(Sheep == passer)
std::cout << "农夫把羊带" << (crossed ? "到河对岸" : "返回") << std::endl;
else if(Cabbage == passer)
std::cout << "农夫把菜带" << (crossed ? "到河对岸" : "返回")<< std::endl;
else if(-1 == passer)
std::cout << "农夫独自" << (crossed ? "到河对岸" : "返回")<< std::endl;

crossed = !crossed;
}
std::cout << "完成!" << std::endl;
}

int main()
{
std::vector<int> vecPath(16, -1); // 记录已经探测状态及转移路径,初始化为-1
findRoute(vecPath);
showRoute(vecPath);

getchar();
return 0;
}


       本算法仅实现了一种可行方案的搜索,并未列出所有的情况,建议大家修改代码进一步实现。

       广度优先算法 作者并没有实现,大家可以从网上搜一下参考代码,自己实现(相信你可以控制在100行以内)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息