仅供学习使用,还请大神多多指点!
传教士与野人
问题描述:M个传教士和N个野人在河的一边,还有一条能载一个人或者两个人的船。找到一个办法让所有的人都渡到河另一岸,要求在任何地方野人数都不能多于传教士的人数(可以只有野人没有传教士)。
问题形式化:1、状态:每个状态有河两岸A与B对应的传教士与野人数和船的位置决定。
2、初始状态:A点M个野人N个传教士船在A侧。
3、后继函数:返回状态取决于船的方向与船在的人
4、目标测试:M个野人和N个传教士都在B侧
5、路径耗散:船渡河的次数
程序设计:1、状态保存在长度为2的数组state[2]中,s[0]与s[1]分别为A侧野人与传教士数量,和一个状态标志表示船的位置。 如何压缩状态?
2、状态转移函数取决于船的方向,船的共有十种方向保存在数组dir[10]中,dir[0]至dir[4]分别表示船从A到B运送一名传教士、一名野人、一名传教士和一名野人、两名传教士、两名野人,同样dir[5]至dir[9]分别表示从B到A运送一名传教士、一名野人、一名传教士和一名野人、两名传教士、两名野人。
3、目标检测函数check()检查目前状态是否已经为目标状态。
源程序:
#include
#include
#include
#include
#include
#define MAXN 1000
using namespace std;
struct STATE
{
int missionary;
int savage;
int boat; //标识船的地点,0表示在左侧,1表示在右侧
STATE(int m,int s,int b)
{
missionary = m;
savage = s;
boat = b;
}
};
int pre[MAXN];
int dir[10][2] = {{-2,0},{-1,-1},{-1,0},{0,-2},{0,-1},{2,0},{1,1},{1,0},{0,2},{0,1}};
int bfs(int num_m,int num_s);
int get_state(STATE state);
bool check(int cur_missionary,int cur_savage,int all_missionary,int all_savage,int next_state);
void trace(int beg_state,int end_state);
int main()
{
int num_m,num_s;
cout << "please input the number of missionary and savage:" << endl;
while(cin >> num_m >> num_s)
{
if(bfs(num_m,num_s))
{
STATE beg(num_m,num_s,0),end(0,0,1);
trace(get_state(beg),get_state(end));
}
else
{
cout << "the problem is unsolvable!" << endl;
}
cout << endl << endl << "please input the number of missionary and savage:" << endl;
}
return 0;
}
int bfs(int num_m,int num_s)
{
STATE fir(num_m,num_s,0);
memset(pre,-1,sizeof(pre));
pre[get_state(fir)] = -2;
queue q;
q.push(fir);
while(!q.empty())
{
STATE cur = q.front();
q.pop();
if(!cur.missionary && !cur.savage)
{
return true;
}
for(int i = 0;i < 10;i++)
{
if(i < 5 && cur.boat || i >= 5 && !cur.boat)
{
continue;
}
STATE next = cur;
next.boat = !cur.boat;
next.missionary += dir[i][0];
next.savage += dir[i][1];
int next_state = get_state(next);
if(check(next.missionary,next.savage,num_m,num_s,next_state))
{
continue;
}
// cout << next.missionary << next.savage << next.boat << endl;
q.push(next);
pre[next_state] = i; //记录方向
}
}
return false;
}
//只是简单的把三个状态压缩到一个十进制数中,暂时没有想到更好的压缩方式,
//因此暂时传教士数与野人数只能小于10.
int get_state(STATE state)
{
return state.missionary * 100 + state.savage * 10 + state.boat;
}
bool check(int cur_missionary,int cur_savage,int all_missionary,int all_savage,int next_state)
{
if(cur_missionary < 0 || cur_missionary > all_missionary || cur_savage < 0 || cur_savage > all_savage) //检查是否越界
{
return true;
}
if((cur_missionary && cur_missionary < cur_savage) || ((all_missionary - cur_missionary) && all_missionary - cur_missionary < all_savage - cur_savage)) //检查是否满足两岸传教士人数不少于野人数
{
return true;
}
if(pre[next_state] != -1) //检查是否已经搜索过此状态
{
return true;
}
return false;
}
void trace(int beg_state,int end_state)
{
stack path;
int state = end_state;
while(state != beg_state) //写的好乱
{
int m = state / 100;
int s = (state / 10) % 10;
int b = state % 10;
int d = pre[state];
path.push(d);
m -= dir[d][0];
s -= dir[d][1];
b = !b;
state = get_state(STATE(m,s,b));
}
while(!path.empty())
{
int d = path.top();
path.pop();
if(d < 5)
cout << "move" << setw(10) << -dir[d][0] << " missionary and" << setw(10) << -dir[d][1] << " savage from A sides to B sides" << endl;
else
cout << "move" << setw(10) << dir[d][0] << " missionary and" << setw(10) << dir[d][1] << " savage from B sides to A sides" << endl;
}
}