使用广度优先搜索解决传教士野人(MC)问题
广度优先搜索算法步骤:
- 把初始节点S0放入OPEN表。
- 如果OPEN表为空,则问题无解,退出。
- 把OPEN表的第一个节点(记为节点n)取出放入CLOSE表。
- 考察节点n是否为目标节点。若是,则求得了问题的解,退出。
- 若节点n不可扩展,则转第2步。
- 扩展节点n,将其子节点放入OPEN表的尾部,并为每一个子节点都配置指向父节点的指针,然后转第2步。
#include <iostream>
#include<vector>
#include <algorithm>
using namespace std;
//定义状态
class bank {
public:
int MissionaryNum;
int CannibalsNum;
int BoatInHere;
int FatherPoint;//存放父节点在close表中的下标
int OrderNumber;//从初始到当前节点的代价
bool operator==(bank bank1);
};
bool bank::operator==(bank bank1)
{
if (this->BoatInHere == bank1.BoatInHere
&& this->CannibalsNum == bank1.CannibalsNum
&& this->MissionaryNum == bank1.MissionaryNum)
{
return true;
}
else
{
return false;
}
}
class MC {
private:
int MissionaryNum;//传教士数量
int CannibalsNum;//野人数量
int BoatNum;//船只数量
vector<bank> BanksOpen;//存放open表
vector<bank> BanksClose;//close表
bank beginPath;//开始的路径点
bank succesPath;//最后的路径点
int BoatMove[500][3];//操作集合:过河的传教士人数和野人人数,出发地:左岸为1,右岸为0;
int BoatMoveSize;
public:
MC(int m, int c, int b);
~MC() {}
//查找所有子节点
void SearchSonPoint();
//判断节点状态是否合法
bool JudgePointState(bank thisBank);
//判断节点是否已生成
bool JudgePointCreated(bank thisBank,int& index,int& who);
//比较该节点本父节点的代价和新父节点代价,本来小,返回 true,否则false
bool JudgeCost(int index, int who, bank newFatherBank);
//判断是否已完成任务
bool JudgeAchievement(bank thisBank2);
//改变所有该节点的子节点的值
void ChangeSonPointCost(int index);
//寻找该属性的索引值
int FoundIndex(bank thisBank);
//返回一个路径点集合
vector<bank> ReturnResult();
//打印列表
void Output();
};
void MC::Output()
{
vector<bank> result = ReturnResult();
if (result.size() == 0)
{
cout << "无解" << endl;
}
else
{
for (int i = result.size() - 1; i > 0; i--)
{
cout << "左岸" << endl;
cout << "修道士:" << result.at(i).MissionaryNum << "人 野人:" << result.at(i).CannibalsNum << "人" << endl;
if (i != 0)
{
if (result.at(i).BoatInHere == 1)
{
cout << abs(result.at(i).CannibalsNum - result.at(i - 1).CannibalsNum) << "个野人从左岸移到右岸" << endl;
cout << abs(result.at(i).MissionaryNum - result.at(i - 1).MissionaryNum) << "个传教士从左岸移到右岸" << endl;
}
else
{
cout << abs(result.at(i).CannibalsNum - result.at(i - 1).CannibalsNum) << "个野人从右岸移到左岸" << endl;
cout << abs(result.at(i).MissionaryNum - result.at(i - 1).MissionaryNum) << "个传教士从右岸移到左岸" << endl;
}
}
cout << "右岸" << endl;
cout << "修道士:" << MissionaryNum - result.at(i).MissionaryNum << "人 野人:" << CannibalsNum - result.at(i).CannibalsNum << "人" << endl << endl;
}
}
}
vector<bank> MC::ReturnResult()
{
vector<bank> result;
if (succesPath == beginPath)
{
}
else
{
result.push_back(succesPath);
bank judge = succesPath;
while (judge.FatherPoint != -1)
{
judge = BanksClose.at(judge.FatherPoint);
result.push_back(judge);
}
}
return result;
}
void MC::ChangeSonPointCost(int index)
{
for (int i = 0; i < BanksOpen.size(); i++)
{
if (BanksOpen.at(i).FatherPoint == index)
{
BanksOpen.at(i).OrderNumber = BanksClose.at(index).OrderNumber + 1;
}
}
for (int i = 0; i < BanksClose.size(); i++)
{
if (BanksClose.at(i).FatherPoint == index)
{
BanksClose.at(i).OrderNumber = BanksClose.at(index).OrderNumber + 1;
int fatherPointIndex = FoundIndex(BanksClose.at(i));
ChangeSonPointCost(fatherPointIndex);
}
}
}
int MC::FoundIndex(bank thisBank)
{
vector<bank>::iterator it = find(BanksClose.begin(), BanksClose.end(), thisBank);
int fatherPoint;
if (it != BanksClose.end())
{
fatherPoint = distance(BanksClose.begin(), it);
}
return fatherPoint;
}
void MC::SearchSonPoint() {
//只有当open表不为空时查找子节点
bank thisBank;//存放当前节点
bank sonBank;//存放生成的子节点
while (BanksOpen.size() > 0)
{
thisBank = BanksOpen.at(0);
if (JudgeAchievement(thisBank))
{
break;
}
BanksOpen.erase(begin(BanksOpen));
BanksClose.push_back(thisBank);//将根节点转移到close表
for (int i = 0; i < BoatMoveSize; i++)
{
//生成节点
if (BoatMove[i][2] != thisBank.BoatInHere)
{
continue;
}
else
{
if (BoatMove[i][2]==1)
{
sonBank.BoatInHere = abs(thisBank.BoatInHere - 1);
sonBank.MissionaryNum = thisBank.MissionaryNum - BoatMove[i][0];
sonBank.CannibalsNum = thisBank.CannibalsNum - BoatMove[i][1];
sonBank.FatherPoint = BanksClose.size() - 1;
sonBank.OrderNumber = thisBank.OrderNumber + 1;
}
else
{
sonBank.BoatInHere = abs(thisBank.BoatInHere + 1);
sonBank.MissionaryNum = thisBank.MissionaryNum + BoatMove[i][0];
sonBank.CannibalsNum = thisBank.CannibalsNum + BoatMove[i][1];
sonBank.FatherPoint = BanksClose.size() - 1;
sonBank.OrderNumber = thisBank.OrderNumber + 1;
}
if (JudgePointState(sonBank))
{
continue;
}
int index, who;
//如果创建了
if (JudgePointCreated(sonBank,index,who))
{
if (JudgeCost(index,who, thisBank))
{
//原来的花费少,所以不用改变父指针
}
else
{
//改变
//在open里
if (who == 0)
{
BanksOpen[index].FatherPoint = BanksClose.size()-1;
BanksOpen[index].OrderNumber = thisBank.OrderNumber + 1;
}
//在close里
else
{
BanksClose[index].FatherPoint = BanksClose.size() - 1;
BanksClose[index].OrderNumber = thisBank.OrderNumber + 1;
int fatherPoint = FoundIndex(sonBank);
ChangeSonPointCost(fatherPoint);
}
}
}
//如果没创建,放在数组后面
else {
BanksOpen.push_back(sonBank);
if (JudgeAchievement(sonBank))
{
succesPath = sonBank;
goto h;
}
}
}
}
}
h:
cout <<"完成查找"<< endl;
}
//判断是否违规
bool MC::JudgePointState(bank thisBank)
{
if (
thisBank.CannibalsNum > thisBank.MissionaryNum && thisBank.MissionaryNum!=0 ||
CannibalsNum- thisBank.CannibalsNum >MissionaryNum- thisBank.MissionaryNum && MissionaryNum - thisBank.MissionaryNum!=0||
thisBank.CannibalsNum>CannibalsNum||
thisBank.CannibalsNum<0||
thisBank.MissionaryNum>MissionaryNum||
thisBank.MissionaryNum<0
)
{
//cout << "违规" << endl;
return true;
}
else
{
//cout << "不违规" << endl;
return false;
}
}
//index是下标,who是那个表
bool MC::JudgePointCreated(bank thisBank, int& index, int& who)
{
for (int i = 0; i < BanksOpen.size(); i++)
{
if (thisBank == BanksOpen.at(i))
{
index = i;
who = 0;
return true;
}
}
for (int i = 0; i < BanksClose.size(); i++)
{
if (thisBank == BanksClose.at(i))
{
index = i;
who = 1;
return true;
}
}
return false;
}
bool MC::JudgeCost(int index,int who, bank newFatherBank)
{
if (who == 0)
{
if (BanksOpen.at(index).OrderNumber<newFatherBank.OrderNumber+1)
{
return true;
}
else
{
return false;
}
}
else
{
if (BanksClose.at(index).OrderNumber < newFatherBank.OrderNumber + 1)
{
return true;
}
else
{
return false;
}
}
}
bool MC::JudgeAchievement(bank thisBank2)
{
//当前的节点是否是最终的节点
if (thisBank2.BoatInHere == 0 && thisBank2.MissionaryNum == 0 && thisBank2.CannibalsNum == 0) {
return true;
}
else
{
return false;
}
//cout << "判断最终节点完成" << endl;
}
MC::MC(int m,int c,int b){
MissionaryNum = m;
CannibalsNum = c;
BoatNum = b;
BoatMoveSize = 0;
//初始化操作集合
int k = 0;
for (int i = 0; i <= b; i++)
{
for (int j = 0; j <= b-i; j++)
{
if (i + j <= b && i+j!=0)
{
BoatMove[k][0] = i;
BoatMove[k][1] = j;
BoatMove[k][2] = 0;
//cout << BoatMove[k][0] <<","<< BoatMove[k][1] << "," << BoatMove[k][2] << endl;
BoatMoveSize += 1;
k++;
}
}
}
for (int i = 0; i <= b; i++)
{
for (int j = 0; j <= b - i; j++)
{
if (i + j <= b && i + j != 0)
{
BoatMove[k][0] = i;
BoatMove[k][1] = j;
BoatMove[k][2] = 1;
//cout << BoatMove[k][0] << "," << BoatMove[k][1] << "," << BoatMove[k][2] << endl;
BoatMoveSize += 1;
k++;
}
}
}
//cout << BoatMoveSize << endl;
//初始化open表,初始状态为n,n,1
bank elem;
elem.BoatInHere = 1;
elem.FatherPoint = -1;
elem.CannibalsNum = CannibalsNum;
elem.MissionaryNum = MissionaryNum;
elem.OrderNumber = 0;
succesPath = elem;
beginPath = elem;
BanksOpen.push_back(elem);
//初始化close表 ,初始为0
//cout << "初始化完成" << endl;
}
int main() {
int M, C, B;
cin >> M;
cin >> C;
cin >> B;
MC mcpro(M, C, B);
mcpro.SearchSonPoint();
mcpro.Output();
return 0;
}
算法写的很垃圾,仅作记载用,有建议或者错误可以留言一起交流