状态空间表示法----野人与修道士

状态空间法的应用

修道士(Missionaries)和野人(Cannibals)问题

   在河的左岸有N个传教士(M)、N个野人(C)和一条船(Boat),传教士们想用这条船把所有人都运过河去,但有以下条件限制:

  (1)修道士和野人都会划船,但船每次最多只能运K个人;

  (2)在任何岸边野人数目都不能超过修道士,否则修道士会被野人吃掉。

   假定野人会服从任何一种过河安排,请规划出一个确保修道士安全过河的计划。

 

约束条件:① M≧C 任何时刻两岸、船上都必须满足传教士人数不少于野人数(M=0时除外,既没有传教士)② M+C≦K 船上人数限制在K以内

求解:传教士与野人全部安全渡到对岸的解决方案。     

 

解:设N=3,K=2(三个M和三个C,每次渡河二人以下)    

 L:左岸,R:右岸,

    B:是否有船(0:无船,1:有船)

                    

 

①状态表示

定义:用三元组(ML,CL,BL)表示左岸状态,其中:

     0≦ML,CL≦3,BL∈{0,1}

如:(0,3,1)表示左岸有三个野人,船在左岸。

M&C的问题描述: 从(3,3,1)到(0,0,0)的状态转换

状态空间:32 种状态,其中:

  12种不合理状态:如(1,0,1)说明右岸有2个M,3个C;4种不可能状态:如(3,3,0)说明所有M和C都在左岸,而船在右岸

∴可用的状态共16种,组成合理的状态空间

②操作集

定义:Pmc操作:从左岸划向右岸

         Qmc操作:从右岸划向左岸

船上人数组合(m,c)共5种应P,Q二种操作

∴系统共有 5×(1,0),(1,1),(2,0),(0,1),(0,2)

∵每一种船上的人数组合同时对2=10种操作(规则)

如:P10:if (ML,CL,BL=1) then (ML-1,CL,BL-1)

        如果船在左岸,那么一个传教士划船到右岸

         Q01:if (ML,CL,BL=0) then (ML,CL+1,BL+1)

        如果船在右岸,那么一个野人划船回到左岸

总共有10种操作

F={P10, P20, P11, P01, P02, Q 10, Q 20, Q11, Q 01, Q 02}

③控制策略

    最短路径有4条,由11次操作构成。

(P11、Q10、P02、Q01、P20、Q11、P20、Q01、P02、Q01、P02)
(P11、Q10、P02、Q01、P20、Q11、P20、Q01、P02、Q10、P11)
(P02、Q01、P02、Q01、P20、Q11、P20、Q01、P02、Q01、P02)
(P02、Q01、P02、Q01、P20、Q11、P20、Q01、P02、Q10、P11)

④状态空间图

状态空间图是一个有向图,图中的节点代表状态,节点之间的连线代表操作,箭头代表状态的转换方向。


package cn.itAI.YeRenAndXiuDaoShi;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

/**
 * 野人与修道士
 * @author 
 *
 */
/**
 * 数据结构分析:使用List对象存储code节点信息
 * @author dell-
 *
 */
public class Main {
	static int N;//N个野人,N个修道士
	static int K;//船上至多有K个人
	static int count=0;
	static List<code>s=new ArrayList<code>();
	public static void main(String[] args) {
		Scanner in=new Scanner(System.in);
		System.out.println("请输入野人/传教士:");
		N=in.nextInt();
		System.out.println("请输入船至多载人的个数:");
		K=in.nextInt();
		code c=new code(N,N,1);//1:船在左岸 0:船在右岸
		s.add(c);
		bfs(c);
		System.out.println("count:"+count);
	}
	public static boolean check(List<code>s,code c){//合法状态的判断
		//1.不可重复
		if(!s.isEmpty()){//包含此元素,表中含重复元素
			for(int i=0;i<s.size();i++){
				if(s.get(i).Boat==c.Boat&&s.get(i).leftC==c.leftC&&s.get(i).leftM==c.leftM){
					return false;
				}
			}
		}
		//2.是否满足m>=c
		if((c.leftM!=0&&c.leftM<c.leftC)){//((N-c.leftM!=0)&&(N-c.leftM<c.leftC))){
			return false;
		}
		if((N-c.leftM!=0)&&(N-c.leftM<N-c.leftC)){
			return false;
		}
		return true;
	}
	/**
	 * 深搜:输出所有满足序列
	 */
	
	public static void bfs(code c){
		if(c.leftC==0&&c.leftM==0&&c.Boat==0){
			for(int i=0;i<s.size();i++){
				s.get(i).toGetString();
			}
			count++;
			System.out.println("==============================");
			return;
		}
		/**
		 * 判断船上至多野人和修道士的数量
		 */
		int k,k1;
		if(c.Boat==1){
			k=c.leftM>=K?K:c.leftM;
			k1=c.leftC>=K?K:c.leftC;
		}
		else{
			k=(N-c.leftM)>=K?K:(N-c.leftM);
			k1=(N-c.leftC)>=K?K:(N-c.leftC);
		}
		/**
		 * 枚举过河的所有情况
		 */
		for(int i=0;i<=k;i++){
			int m=i;//船上修道士数量
			for(int j=0;j<=((K-m)>=k1?k1:(K-m));j++){
				int n=j;//船上野人数量
				int tleftM,tleftC,tflag;
				if(m==0&&n==0) continue;
				code ct;
				if(c.Boat==1){//当前状态是合法条件,船在左岸可以开往右岸
					tflag=0;//m,n是船上的野人和修道士
					tleftM=N-((N-c.leftM)+m);//
					tleftC=N-((N-c.leftC)+n);
					ct=new code(tleftM,tleftC,tflag);
					if(check(s,ct)){
						s.add(ct);//加入表
						bfs(ct);
						s.remove(ct);//回溯
					}	
				}else{//右岸-》左岸
					tflag=1;
					tleftM=N-((N-c.leftM)-m);
					tleftC=N-((N-c.leftC)-n);
					ct=new code(tleftM,tleftC,tflag);
					if(check(s,ct)){
						s.add(ct);
						bfs(ct);
						s.remove(ct);
					}							
				}
			}
		}
		return ;
	}	
	
} 
class code{
	int leftM;//左岸修道士
	int leftC;//左岸野人
	int Boat;//船的状态
	public code(int leftM,int leftC,int Boat){
		this.leftC=leftC;
		this.leftM=leftM;
		this.Boat=Boat;
	} 
	public void toGetString(){
		System.out.print("修道士:"+this.leftM+" 野人: "+this.leftC+" 船的方向:");
		if(this.Boat==1){
			System.out.println("左岸-->右岸");
		}else{
			System.out.println("右岸-->左岸");
		}
	}
}


  • 18
    点赞
  • 137
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,关于状态空间法解决野人修道士问题的 Python 实现,我可以为您提供一些思路和代码示例。 首先,我们需要定义每个状态的数据结构。在这个问题中,每个状态由三个参数组成:左岸的修道士人数、左岸的野人人数、船只的位置(左岸或右岸)。我们可以用一个元组 `(m, c, b)` 来表示一个状态,其中 `m` 表示左岸的修道士人数,`c` 表示左岸的野人人数,`b` 表示船只的位置,`b=0` 表示船只在左岸,`b=1` 表示船只在右岸。 接下来,我们需要定义状态转移函数。对于每个状态,有两种可能的操作:将船只移动到对岸,或者将船只上的一名修道士和一名野人移动到对岸。我们可以用一个函数 `get_successors(state)` 来计算一个状态的所有后继状态。 然后,我们可以使用广度优先搜索算法来遍历状态空间,找到从起始状态到达目标状态的最短路径。我们可以用一个队列来保存待处理的状态,用一个字典来记录每个状态的前驱状态,最后回溯得到一条路径。 下面是一个简单的 Python 实现,您可以参考一下: ``` python from collections import deque def get_successors(state): m, c, b = state successors = [] if b == 0: # 船只在左岸 for i in range(3): for j in range(3): if i + j >= 1 and i + j <= 2: successors.append((m-i, c-j, 1)) else: # 船只在右岸 for i in range(3): for j in range(3): if i + j >= 1 and i + j <= 2 and i + j <= m + c: successors.append((m+i, c+j, 0)) return successors def bfs(start, end): queue = deque([start]) visited = set([start]) predecessors = {start: None} while queue: state = queue.popleft() if state == end: path = [] while state: path.append(state) state = predecessors[state] return list(reversed(path)) for successor in get_successors(state): if successor not in visited: queue.append(successor) visited.add(successor) predecessors[successor] = state return None start = (3, 3, 0) end = (0, 0, 1) path = bfs(start, end) if path: for state in path: print(state) else: print('No path found.') ``` 这段代码可以解决野人修道士问题,起始状态为 `(3, 3, 0)`,目标状态为 `(0, 0, 1)`。程序输出的是一条从起始状态到目标状态的最短路径,其中每个元素表示一个状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值