目录
问题描述
有三个牧师和三个野人过河,只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于牧师的认输,那么牧师就会有危险.找出一种按的渡河方法。
将该问题转变为:假如有n个牧师和n个野人准备渡河,但只有一条能容纳c个人的小船,为了防止野人侵犯牧师,要求无论在何处,牧师的人数不得少于野人的人数(除非牧师人数为0),且假定两种人都会划船,试设计一个算法,确定它们能否渡过河去,若能,则给出一只小船来回次数最少的最佳方案。
基本要求
输入:牧师人数(即野人人数)n,小船一次至多载客人数c。
输出:若问题无解,则显示“渡河失败”信息,否则,输出一组最佳方案,用三组(X1,X2,X3)表示渡河过程中的状态。并用箭头输出这些状态之间的迁移:目的状态<- <-中间状态 <- <-初始状态。
例:当n=2,c=2时,输出 000<-021<-211<-110<-221
上述(X1,X1,X2)表示渡河过程中各个状态。
其中:X1表示始岸上牧师人数,X2表示始岸上野人人数,
X3表示小船位置,(0-在目的岸,1-在起始岸 )
c++代码示例
#include <iostream>
#include<queue>
using namespace std;
struct States
{
int X1;//起始岸上的牧师人数
int X2;//起始岸上的野人人数
int X3;//小船现在位置(1表示起始岸,0表示目的岸)
int treedepth;//该状态第一次出现的最低层
States * pre;//指向前驱的指针
States():treedepth(0){}
};
States * hasgone[1000];//已经走过的状态,用于避免以后重复
int num = 0;// 已经走过的状态数量
int n = 0;//牧师和野人的总人数
int c = 0;//小船上可承载人数
queue<States *> que;//队列,用于进行广度优先搜索
//保存路径,以及它对应的船来回次数
States *answer[1000];
int num_answer = 0;// 路径数量
int step_num[1000];//路径对应的步数
//int tree_depth = 0;
queue<States*> rubbish;//垃圾队列,用来存放最后运算完未delete的数据
States* Start2Destination(States* pre,int x1, int x2)//now当前状态,从开始到目的地(x1是船上牧师人数,x2是船上野人人数)
{
int Start_Priests= pre->X1 - x1;//现在岸上的牧师人数
int Start_savages= pre->X2 - x2;//现在岸上的野人人数
int End_Priests = n-pre->X1 + x1;//现在目的地的牧师人数
int End_savages = n-pre->X2 +x2;//现在目的地的野人人数
if ((x1 >= x2||x1==0)&& (Start_Priests >= Start_savages|| Start_Priests==0) &&( End_Priests >= End_savages|| End_Priests==0))//符合要求
{
States * now = new States;
now->pre = pre;
now->X1 = Start_Priests;
now->X2 = Start_savages;
now->treedepth = pre->treedepth + 1;
now->X3 = 0;
return now;
}
return NULL; // 不符合要求,返回空指针
}
States* Destination2Start(States* pre,int x1, int x2)//从目的地到开始地(x1是船上牧师人数,x2是船上野人人数)
{
int Start_Priests = pre->X1 +x1;
//现在岸上的牧师人数
int Start_savages = pre->X2 + x2;
//现在岸上的野人人数
int End_Priests = n - pre->X1 - x1;
//现在目的地的牧师人数
int End_savages = n - pre->X2 - x2;
//现在目的地的野人人数
if ((x1 >= x2 || x1 == 0) && (Start_Priests >= Start_savages || Start_Priests == 0) && (End_Priests >= End_savages || End_Priests == 0))//符合要求
{
States * now = new States;
now->pre = pre;
now->X1 = Start_Priests;
now->X2 = Start_savages;
now->treedepth = pre->treedepth + 1;
now->X3 = 1;
return now;
}
return NULL;// 不符合要求,返回空指针
}
void show(States * states, int time)
{
if (states == NULL)
{
step_num[num_answer] = time;
cout << endl;
}
else
{
show(states->pre, ++time);
cout << "(" << states->X1 << "," << states->X2 << "," << states->X3 << ")"<<"<-<-";
}
}
bool Hasgone(States* one)//判断这个是否前面已经走过的
{
for (int i = 0; i < num; i++)
{
if (one->X1 == hasgone[i]->X1&&one->X2 == hasgone[i]->X2&&one->X3 == hasgone[i]->X3&&one->tree_depth>hasgone[i]->tree_depth)//有重复的,且不再同一层,删除new的东西,return,true
{
delete one;
return true;
}
}
//该状态之前未走过
//加入hasgone里面并num++
hasgone[num] = one;
num++;
return false;
}
int route = 0;
void Nextstep()
{
if (que.empty()) return;//队列为空
States*pre = que.front();
rubbish.push(pre);
que.pop();
if (pre->X1 == 0 && pre->X2 == 0 && pre->X3 == 0)//路程已经走完了
{
route++;
cout << "第"<<route<<"条路径:";
show(pre,0);
cout << " 长度为" << step_num[num_answer] << endl;
answer[num_answer] = pre;
num_answer++;
Nextstep();
}
//if (Hasgone(pre)) return;//这个状态之前走过了,返回
if (pre->X3 == 1)//在起始岸
{
int Max1 = pre->X1 > c ? c : pre->X1;
int Max2 = pre->X2 > c ? c : pre->X2;
for (int i = 0; i <= Max1; i++)
{
for (int j = 0; j <= Max2; j++)
{
if (i + j <= c&&i+j>=1)
{
States* now=Start2Destination(pre,i,j);
if (now!=NULL&&!Hasgone(now))
{
que.push(now);
}
}
}
}
Nextstep();
}
else//在目的岸
{
int Max1 = n - pre->X1 > c ? c : n-pre->X1;
int Max2 = n - pre->X2 > c ? c : n-pre->X2;
for (int i = 0; i <= Max1; i++)
{
for (int j = 0; j <= Max2; j++)
{
if (i + j <= c && i + j >= 1)
{
States* now = Destination2Start(pre, i, j);
if (now != NULL && !Hasgone(now))
{
que.push(now);
}
}
}
}
Nextstep();
}
}
// 释放内存
void delete_rubbish()
{
while (!rubbish.empty())
{
States*del = rubbish.front();
rubbish.pop();
delete del;
}
}
int main()
{
cout << "请输入牧师和野人的人数";
cin >> n;
cout << "请输入小船上能承载的人数" ;
cin >> c;
States* initial = new States();
initial->X1 = n;// 起始岸上的牧师人数
initial->X2 = n;
initial->X3 = 1;
initial->treedepth = 0;
initial->pre = NULL;
que.push(initial);// 将起始状态加入队列
Hasgone(initial);
Nextstep();
if (num_answer == 0) cout << "渡河失败" << endl;
else cout << "渡河成功" << endl;
delete_rubbish();
system("pause");
}