本文出处:http://blog.csdn.net/xizhibei
=============================
M个传教士和C个野人(Missionaries and Cannibals)过河,显然必须要M>=C,只有一艘载重为2的小船,野人会听从传教士的安排,并且野人和传教士都会划船,但是,在河的两岸不能出现野人比传教士多的情况,否则野人就会吃传教士。
好了,现在,编程解决这个问题,安排他们全部过河,并保证传教士的安全。
嗯,据说这是个经典的人工智能问题,显然,解决方法是搜索状态空间。于是各种方法就出来了,BFS、DFS还有大名鼎鼎的A*。
首先,规定下状态空间两岸以及船上的传教士还有野人的数量,船的朝向以及,父状态的Index。
public int lm,lc;//我用left以及right表示这边的岸和对岸了 public int rm,rc; public int m_in_ship; public int c_in_ship; public int parent; public int direction;//船的朝向
1、(0,0)开始状态
2、(0,1)
3、(1,0)
4、(1,1)
5、(2,0)
6、(0,2)
以上,除了开始状态,2-6状态都会在每次抵达岸边后开始互相转换,这里规定不能转换为相同状态,因为这样的话就无意义了,等于坐了无用功,对解决问题没有帮助。
好了,针对每次的状态转换,我用一个四维数组来表示:
private static int[][][][] Transform = { {//0 {//0,0 {2,0},{0,2},{1,0},{0,1},{1,1} }, {//0,1 {1,1},{2,0},{0,2},{1,0} }, {//0,2 {1,1},{2,0},{1,0},{0,1} } }, {//1 {//1,0 {1,1},{2,0},{0,2},{0,1} }, {//1,1 {2,0},{0,2},{1,0},{0,1} }, {//1,2 {} } }, {//2 {//2,0 {1,1},{0,2},{1,0},{0,1} }, {//2,1 {} }, {//2,2 {} } } };
public List<State> getChildren(State s,int parent){ List<State> cs = new ArrayList<State>(); int m = s.m_in_ship; int c = s.c_in_ship; int[][] trans = Transform[m][c]; if(s.direction == State.TO){//到对岸 for(int i = 0;i < trans.length;i++){ if(s.rm >= trans[i][0] - m && s.rc >= trans[i][1] - c) cs.add( new State(s.lm,s.lc,s.rm + m - trans[i][0], s.rc + c - trans[i][1], trans[i][0], trans[i][1],parent,State.FROM) ); } }else{//State.FROM //开始或从对岸回来 for(int i = 0;i < trans.length;i++){ if(s.lm >= trans[i][0] - m && s.lc >= trans[i][1] - c) cs.add( new State(s.lm + m - trans[i][0],s.lc + c - trans[i][1], s.rm,s.rc, trans[i][0], trans[i][1],parent,State.TO) ); } } return cs; }
public void search(int M,int C){ List<State> states = new ArrayList<State>(); //List<State> q = new ArrayList<State>(); PriorityQueue<State> q = new PriorityQueue<State>();//这里用优先队列实现状态的选取,你也可以试试BFS,用队列实现即可 HashSet<State> sset = new HashSet<State>();//用来判断状态是否重复 State init = new State(M,C,0,0,0,0,-1,State.START); q.add(init); sset.add(init); while(!q.isEmpty()){ //State s = q.get(0); //q.remove(0); State s = q.poll(); states.add(s); if(s.lc == 0 && s.lm == 0){ System.out.println("Got it!"); break; } List<State> cs = getChildren(s,states.size() - 1); for(int i = 0;i < cs.size();i++){ State c = cs.get(i); State np= c.getNoParentState(); if(((c.lc <= c.lm || c.lc <= 2 && c.lm == 0) //判断是否安全 && (c.rc <= c.rm || c.rc <= 2 && c.rm == 0)) && !sset.contains(np)){ q.add(c); sset.add(np); } } if(q.size() > 10000){//状态太多就没必要搜索了 System.out.println("Error!!!"); return; } } List<String> o = new ArrayList<String>(); int idx = states.size() - 1; System.out.println("Total states: " +(idx+1)); while(idx != -1){ State s = states.get(idx); o.add(s.toString()); //System.out.println(s.toString()); idx = s.parent; } System.out.println("Total steps: " + (o.size() - 1));//去除开始状态 for(int i = o.size() - 1;i >= 0;i--){ System.out.println(o.get(i)); } }
好了,最后把状态的完整实现贴出来:
private class State implements Comparable<State>{ public static final int TO = 0; public static final int FROM = 1; public static final int START = 2; public int lm,lc; public int rm,rc; public int m_in_ship; public int c_in_ship; public int parent; public int direction; public State(int lm,int lc,int rm,int rc,int m_in_ship,int c_in_ship,int parent,int direction){ this.lm = lm; this.lc = lc; this.rm = rm; this.rc = rc; this.m_in_ship = m_in_ship; this.c_in_ship = c_in_ship; this.parent = parent; this.direction = direction; } public State getNoParentState(){//显然,需要把父节点的idx去掉才能判断是否重复 return new State(lm,lc,rm,rc,m_in_ship,c_in_ship,-1,direction); } public String toString(){ if(direction == State.TO) return lm + "," + lc + " =" + m_in_ship + "," + c_in_ship + "=> "+ rm + "," + rc; else if(direction == State.FROM) return lm + "," + lc + " <=" + m_in_ship + "," + c_in_ship + "= "+ rm + "," + rc; else return lm + "," + lc + " =" + m_in_ship + "," + c_in_ship + "= "+ rm + "," + rc; } @Override public int compareTo(State s) {//A*实现的关键,状态的比较,用来启发搜索 int sum1 = 2*lm + 2*lc + m_in_ship + c_in_ship; int sum2 = 2*s.lm + 2*s.lc + s.m_in_ship + s.c_in_ship; return sum1 - sum2; } }