问题提出:
对象:农夫、狼、羊、白菜
目标:农夫想划船 把狼、羊、菜和自己运到河对岸,船比较小,农夫每次只能运一种东西过河
约束条件:如果没有农夫看着,羊会偷吃菜,狼会吃羊
考虑一种方法,让农夫能够安全地安排过河。
解题思路:
实际上这是一个状态转换问题,也许做过这个题的童鞋都能给出一个正确答案,比如:
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行以内)。