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

状态空间法的应用

修道士(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
    点赞
  • 138
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值