本文出处: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 getChildren(State s,int parent){
List cs = new ArrayList();
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 states = new ArrayList();
//List q = new ArrayList();
PriorityQueue q = new PriorityQueue();//这里用优先队列实现状态的选取,你也可以试试BFS,用队列实现即可
HashSet sset = new HashSet();//用来判断状态是否重复
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 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 o = new ArrayList();
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{
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;
}
}