分享--过河问题的算法--有限状态机

jerry 2005-04-11 11:27:06
过河问题大家都知道,不多说了.
解决这个问题的经典方法就是使用有限状态机. 根据人,狼,羊,菜,在不同河岸,可以抽象出N种不同的状态.某些状态之间可以转换. 这些转换就是运算了. 我们的目的就是找到一组这样的运算,可以从初始状态转换到终止状态. 其间的状态必需都是合法的. C++代码如下:

/*-------------------------------------
农夫,狼,羊, 菜,过河
--------------------------------------*/
#include <list>
#include <iostream>


enum PLACE
{
NO, // 没有过河的
YES, // 已经过河了
};

// 状态
typedef struct
{
PLACE man; // 人的状态
PLACE wolf; // 狼的状态
PLACE sheep; // 羊有状态
PLACE menu; // 菜的状态
} STATUS;


const STATUS c_start = {NO,NO,NO,NO}; // 起始状态
const STATUS c_end = {YES,YES,YES,YES}; // 终止状态

// 结点(保存转换路径)
typedef struct TURN{
STATUS status;
TURN* p;
}TURN;

typedef std::list<TURN> LIST_TURN;
typedef std::list<TURN*> LIST_NODE;

// 判断两个状态是否一致
bool operator == (const STATUS& sa, const STATUS& sb)
{
return sa.man == sb.man && sa.wolf == sb.wolf &&
sa.sheep == sb.sheep && sa.menu == sb.menu;
}

// 查找状态是否已经存在
bool Find(LIST_TURN& ls, const STATUS& s)
{
LIST_TURN::iterator it = ls.begin();
for(;it!=ls.end(); it++)
{
if((*it).status==s)
return true;
}
return false;
}

// 状态转换运算
enum OPERATOR
{
MAN_GO, // 人过河
MAN_GO_WITH_WOLF, // 人带狼过河
MAN_GO_WITH_SHEEP, // 人带羊过河
MAN_GO_WITH_MENU, // 人带菜过河

MAN_BACK, // 人回来
MAN_BACK_WITH_WOLF, // 人带狼回来
MAN_BACK_WITH_SHEEP, // 人带羊回来
MAN_BACK_WITH_MENU, // 人带菜回来

OP_FIRST = MAN_GO,
OP_LAST = MAN_BACK_WITH_MENU,
};

// 状态转换
bool StatusTurn(const STATUS& src, OPERATOR op, STATUS& desc)
{
switch(op)
{
case MAN_GO: // 人过河
if(src.man == NO) {
desc = src;
desc.man = YES;
return true;
}
break;
case MAN_GO_WITH_WOLF: // 人带狼过河
if(src.man == NO && src.wolf == NO)
{
desc = src;
desc.man = YES;
desc.wolf = YES;
return true;
}
break;
case MAN_GO_WITH_SHEEP: // 人带羊过河
if(src.man == NO && src.sheep == NO)
{
desc = src;
desc.man = YES;
desc.sheep = YES;
return true;
}
break;
case MAN_GO_WITH_MENU: // 人带菜过河
if(src.man == NO && src.menu == NO)
{
desc = src;
desc.man = YES;
desc.menu = YES;
return true;
}
break;
case MAN_BACK: // 人回来
if(src.man == YES) {
desc = src;
desc.man = NO;
return true;
}
break;
case MAN_BACK_WITH_WOLF: // 人带狼回来
if(src.man == YES && src.wolf == YES) {
desc = src;
desc.man = NO;
desc.wolf = NO;
return true;
}
break;
case MAN_BACK_WITH_SHEEP: // 人带羊回来
if(src.man == YES && src.sheep == YES) {
desc = src;
desc.man = NO;
desc.sheep = NO;
return true;
}
break;
case MAN_BACK_WITH_MENU: // 人带菜回来
if(src.man == YES && src.menu == YES) {
desc = src;
desc.man = NO;
desc.menu = NO;
return true;
}
break;

}

return false;
}

// 状态检测(检测合法的状态)
bool StatusTest(const STATUS& s)
{
// 人不在的时候,狼会吃羊,羊会吃菜
if(s.man != NO){
if(s.wolf == NO && s.sheep == NO || s.sheep == NO && s.menu == NO)
return false;

}
if(s.man != YES){
if(s.wolf == YES && s.sheep == YES || s.sheep == YES && s.menu == YES)
return false;
}

return true;
}


int main()
{
LIST_TURN history; // 已经搜索过的状态
LIST_NODE active; // 需要搜索的状态

// 生成起始结点
TURN turn;
turn.status = c_start;
turn.p = 0;
history.push_back(turn);
active.push_back(&history.back());

// 初始化
bool bFind = false;
TURN* pend = 0;

// 状态转换图搜索(广度优先,求转换次数最少的路径)
while(!active.empty())
{
// 待搜索的当前结点
TURN* pact = active.front();
active.pop_front();

// 如果等于终止状态,则搜索成功
if(pact->status == c_end)
{
bFind = true;
pend = pact;
break;
}

// 计算当前状态的所有可能的下一个状态
for(OPERATOR op = OP_FIRST; op <= OP_LAST; op=(OPERATOR)(op+1))
{
TURN next;
// 下一个状态满足:
// (1) 有一个转换运算从当前状态转换到此状态
// (2) 此状态是有效的状态
// (3) 此状态是一个新状态.已经搜索过的状态不用再搜索
if(StatusTurn(pact->status, op, next.status) &&
StatusTest(next.status) && !Find(history, next.status))
{
// 为搜索图添加下一个结点
next.p = pact;
history.push_back(next);
active.push_back(&history.back());
}
}
}

// 如果找到
if(bFind)
{
// 计算路径
active.clear();
TURN* p = pend;
while(p)
{
active.push_front(p);
p = p->p;
}

// show node
for(LIST_NODE::iterator it = active.begin(); it != active.end(); it++)
{
if((*it)->status.man == NO) std::cout << "[人]"; else std::cout << "[..]";
if((*it)->status.wolf == NO) std::cout << "[狼]"; else std::cout << "[..]";
if((*it)->status.sheep == NO) std::cout << "[羊]"; else std::cout << "[..]";
if((*it)->status.menu == NO) std::cout << "[菜]"; else std::cout << "[..]";
std::cout << " <=====> ";
if((*it)->status.man == YES) std::cout << "[人]"; else std::cout << "[..]";
if((*it)->status.wolf == YES) std::cout << "[狼]"; else std::cout << "[..]";
if((*it)->status.sheep == YES) std::cout << "[羊]"; else std::cout << "[..]";
if((*it)->status.menu == YES) std::cout << "[菜]"; else std::cout << "[..]";

std::cout << std::endl;
}
}
else
{
std::cout<<"Not Found!" << std::endl;
}

return 0;
}

...全文
327 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
rocklabzhang 2005-04-12
  • 打赏
  • 举报
回复
mark
jerry 2005-04-12
  • 打赏
  • 举报
回复
to: dongpy(51-->ARM)

第一次写状态机,我也不大懂. 能不能具体说一下状态处理器这种方式? 谢谢
dongpy 2005-04-12
  • 打赏
  • 举报
回复
状态机这种模式很不错,楼主的状态机是最经典的那种。

如果状态之间关系复杂,用switch就比较死板了,应该用状态处理器的方式,更灵活。
pcboyxhy 2005-04-12
  • 打赏
  • 举报
回复
一个广搜而已
搞的那么神呼神呼的题目。

如果用A*会不会好一点呢?
hywhy 2005-04-12
  • 打赏
  • 举报
回复
强!!!!!!!!!!!!!!!!!!!!!!!1
hywhy 2005-04-12
  • 打赏
  • 举报
回复
这个程序编译是肯定又错的吧

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧