过河问题——人工智能

目录

问题描述

基本要求

c++代码示例


问题描述

有三个牧师和三个野人过河,只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于牧师的认输,那么牧师就会有危险.找出一种按的渡河方法。

将该问题转变为:假如有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");
}

### 使用AI算法解决经典过河问题 经典的传教士与野人过河问题是约束满足问题的一个典型例子。通过引入人工智能技术,特别是搜索算法,可以有效地找到解决方案。 #### 1. 定义状态空间 在该问题中,状态可以通过以下几个变量来定义: - `iMissionary` `iSavage` 表示当前岸边的传教士野人的数量。 - `oppo_m` `oppo_s` 表示对岸的传教士野人的数量。 - 船的位置以及船上的人数也需要被考虑。 每一步的状态转移由船载人数及其组成决定。合法状态需满足条件:任意时刻,两岸及船上,传教士的数量都不小于野人(除非某岸无传教士)。这些规则已在给定伪代码中体现[^1]。 #### 2. 应用搜索算法 常见的用于求解此类问题的AI方法包括广度优先搜索(BFS),深度优先搜索(DFS)以及其他启发式搜索如A*算法。 ##### 广度优先搜索 (BFS) BFS是一种逐层扩展节点的方式,它能够保证找到最短路径解法。对于每一个可能的动作组合`(x,y)`——即分别表示移动到另一侧的传教士野人的数量,如果动作有效,则创建一个新的子状态并加入队列等待进一步探索。 以下是基于Python实现的一个简单版本: ```python from collections import deque def is_valid_state(state): missionary_left, savage_left, boat_pos = state[:3] missionary_right = 3 - missionary_left savage_right = 3 - savage_left if missionary_left < 0 or savage_left < 0 or missionary_right < 0 or savage_right < 0: return False if missionary_left > 0 and missionary_left < savage_left: return False if missionary_right > 0 and missionary_right < savage_right: return False return True def bfs(): initial_state = (3, 3, 'left', []) queue = deque([initial_state]) visited_states = set() while queue: current_state = queue.popleft() m_left, s_left, b_position, path = current_state if (m_left == 0 and s_left == 0): print("Solution found:", path) return path for move in [(1,0),(2,0),(0,1),(0,2),(1,1)]: new_m_left = m_left - move[0]*(-1)**(b_position=='right') new_s_left = s_left - move[1]*(-1)**(b_position=='right') next_b_position = 'right' if b_position == 'left' else 'left' new_state = (new_m_left, new_s_left, next_b_position, path + [move]) if not is_valid_state(new_state[:-1]): continue if tuple(new_state[:-1]) not in visited_states: visited_states.add(tuple(new_state[:-1])) queue.append(new_state) bfs() ``` 上述脚本实现了基本框架下的BFS策略,并打印出成功穿越河流所需的最小步数组合[^2]。 #### 3. 启发式优化(A*) 虽然BFS能提供最优解,但对于更复杂的情况可能会消耗过多资源。因此采用带有估价函数f(n)=g(n)+h(n)形式的A*算法会更加高效,其中g(n)代表从初始结点到达n的实际代价,h(n)则是估计剩余距离的成本。 在这里我们可以设定简单的曼哈顿距离作为启发值计算方式之一: \[ h(n) = |M_{start} - M_{goal}| + |C_{start} - C_{goal}| \] 这有助于减少不必要的分支遍历次数从而提升效率。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值