如上图,入口2011,经过加减乘除后到达出口2012。
规则:1、不能连续执行两次操作;2、方向必须”向前“。
具体例子:从/2到*3后,只能执行-5,不能掉头执行/2或者+7(规则2),更加不能够再次*3(规则1)
在微博 http://weibo.com/1915548291/z4eTPtAnv 发现此问题。此微博的评论有其他同学更高效的算法(A*算法、双向BFS etc.),有兴趣请点击~
问题源于 http://www2.stetson.edu/~efriedma/holiday/2011/index.html
在Matrix67的博客有介绍 http://www.matrix67.com/blog/archives/4790
基本思路:使用BFS或者DFS搜索以2011为根的树即可。
另外:1、根据规则2,必须要处理好计算的方向问题:例如 *3后有可能从左侧进入-5,也有可能从右侧。
2、注意到 2012不能被3整除,所以唯一的可能的最后操作就是 “左侧进入”的 *3 或者 +7 或者 /2 之后的-5。
3、进行简单的重复状态判定。为了简便起见,这里仅判定 数值+操作 是否重复了:对这两个因子做简单的哈希处理即可。
======================
枚举 操作+方向:
private enum OperationAndDirection {
START(0),
ADD7_LEFT(1),ADD7_RIGHT(2),
SUB5_LEFT(3),SUB5_RIGHT(4), // SUB5_RIGHT is the ONLY OandD that leads to the exit 2012.
MUL3_LEFT(5),MUL3_RIGHT(6),
DIV2_LEFT(7),DIV2_RIGHT(8);
public final int value;
OperationAndDirection(int value) {
this.value = value;
}
}
=====================
内部类Node 及 其他数据结构:
private Queue<Node> q = new LinkedList<Node>(); // used in BFS
private Set<Integer> values = new HashSet<Integer>();
// Keep track of the number of visited nodes.
private long visitedNodes = 0;
// Keep track of the number of solutions within 30 levels of DFS tree.
private int solutions= 0;
private class Node{
public int value;
public OperationAndDirection od;
public Node parent;
public Node(int v,OperationAndDirection od,Node p){
this.value = v;
this.parent = p;
this.od = od;
}
@Override
public String toString() {
return value+":"+od;
}
@Override
public boolean equals(Object obj) {
return this.value == ((Node)obj).value && this.od == ((Node)obj).od;
}
@Override
public int hashCode() {
return this.value*10 + this.value + this.od.value;
}
}
======================
使用BFS求解:
private void addOrNot(Node n){
if(!values.contains(n.hashCode())){
values.add(n.hashCode());
q.add(n);
visitedNodes++;
}
}
// A simple solution is as follows:
// Use BFS to generate numbers on each level, and test whether a number is 2012,
// and the number is calculated either by *3 or -5.
// In this case, because 2012 cannot be divided by 3,
// thus only subtracting 5 is a valid operation. That is, the final calculation should be 2017 - 5 = 2012
// Also notice that -5 should be a from-left-to-right operation.
// That is, after -5, the direction is going to the exit, not going back to the entrance.
public void compute(int entrance,int exit){
Node start = new Node(entrance,OperationAndDirection.START,null);
Node n1 = new Node(entrance+7,OperationAndDirection.ADD7_RIGHT,start);
Node n2 = new Node(entrance/2,OperationAndDirection.DIV2_RIGHT,start);
addOrNot(start);
addOrNot(n1);
addOrNot(n2);
// BFS
while(!q.isEmpty()){
Node n = q.poll();
int p = n.value;
if (n.value == exit && n.od == OperationAndDirection.SUB5_RIGHT){
Stack<OperationAndDirection> operations = new Stack<OperationAndDirection>();
while(n.parent!=null){
operations.push(n.od);
n = n.parent;
}
System.out.print(entrance);
while(!operations.empty()) {
OperationAndDirection op = operations.pop();
if(op == OperationAndDirection.ADD7_LEFT || op == OperationAndDirection.ADD7_RIGHT){
System.out.print(" +7");
}else if(op == OperationAndDirection.SUB5_LEFT || op == OperationAndDirection.SUB5_RIGHT){
System.out.print(" -5");
}else if(op == OperationAndDirection.MUL3_LEFT || op == OperationAndDirection.MUL3_RIGHT){
System.out.print(" *3");
}else if(op == OperationAndDirection.DIV2_LEFT || op == OperationAndDirection.DIV2_RIGHT){
System.out.print(" /2");
}
}
System.out.println(" = " + exit);
System.out.println(visitedNodes + " nodes were visited.");
return;
}
switch (n.od) {
case SUB5_RIGHT:
addOrNot(new Node(p*3,OperationAndDirection.MUL3_LEFT,n));
break;
case SUB5_LEFT:
addOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n));
addOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n));
addOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n));
break;
case ADD7_RIGHT:
addOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n));
addOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n));
addOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n));
break;
case ADD7_LEFT:
addOrNot(new Node(p/2,OperationAndDirection.DIV2_RIGHT,n));
break;
case MUL3_RIGHT:
addOrNot( new Node(p-5,OperationAndDirection.SUB5_LEFT,n));
break;
case MUL3_LEFT:
addOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n));
addOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n));
addOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n));
break;
case DIV2_RIGHT:
addOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n));
addOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n));
addOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n));
break;
case DIV2_LEFT:
addOrNot(new Node(p+7,OperationAndDirection.ADD7_RIGHT,n));
break;
default:
break;
}
}
}
====================
使用DFS求解:
1、注意这里限定了搜索深度为40
2、在每层搜索完毕删除该结点在哈希表中的值,以输出其他解
public From2011To2012_SimpleDFS(int en, int ex) {
this.entrance = en;
this.exit = ex;
}
private void dfsOrNot(Node n,int level){
if(!values.contains(n.hashCode())){
values.add(n.hashCode());
dfs(n,level);
values.remove(n.hashCode());
}
}
private void dfs(Node n, int level){
visitedNodes++;
if (n.value == exit && n.od == OperationAndDirection.SUB5_RIGHT){
solutions++;
Stack<OperationAndDirection> operations = new Stack<OperationAndDirection>();
while(n.parent!=null){
operations.push(n.od);
n = n.parent;
}
System.out.print(this.entrance);
while(!operations.empty()) {
OperationAndDirection op = operations.pop();
if(op == OperationAndDirection.ADD7_LEFT || op == OperationAndDirection.ADD7_RIGHT){
System.out.print(" +7");
}else if(op == OperationAndDirection.SUB5_LEFT || op == OperationAndDirection.SUB5_RIGHT){
System.out.print(" -5");
}else if(op == OperationAndDirection.MUL3_LEFT || op == OperationAndDirection.MUL3_RIGHT){
System.out.print(" *3");
}else if(op == OperationAndDirection.DIV2_LEFT || op == OperationAndDirection.DIV2_RIGHT){
System.out.print(" /2");
}
}
System.out.println(" = " + this.exit);
}
if(level == 40){
return;
}else{
int p = n.value;
switch (n.od) {
case START:
dfsOrNot(new Node(entrance+7,OperationAndDirection.ADD7_RIGHT,n),level+1);
dfsOrNot(new Node(entrance/2,OperationAndDirection.DIV2_RIGHT,n),level+1);
break;
case SUB5_RIGHT:
dfsOrNot(new Node(p*3,OperationAndDirection.MUL3_LEFT,n),level+1);
break;
case SUB5_LEFT:
dfsOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n),level+1);
dfsOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n),level+1);
dfsOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n),level+1);
break;
case ADD7_RIGHT:
dfsOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n),level+1);
dfsOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n),level+1);
dfsOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n),level+1);
break;
case ADD7_LEFT:
dfsOrNot(new Node(p/2,OperationAndDirection.DIV2_RIGHT,n),level+1);
break;
case MUL3_RIGHT:
dfsOrNot( new Node(p-5,OperationAndDirection.SUB5_LEFT,n),level+1);
break;
case MUL3_LEFT:
dfsOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n),level+1);
dfsOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n),level+1);
dfsOrNot(new Node(p/2,OperationAndDirection.DIV2_LEFT,n),level+1);
break;
case DIV2_RIGHT:
dfsOrNot(new Node(p+7,OperationAndDirection.ADD7_LEFT,n),level+1);
dfsOrNot(new Node(p-5,OperationAndDirection.SUB5_RIGHT,n),level+1);
dfsOrNot(new Node(p*3,OperationAndDirection.MUL3_RIGHT,n),level+1);
break;
case DIV2_LEFT:
dfsOrNot(new Node(p+7,OperationAndDirection.ADD7_RIGHT,n),level+1);
break;
default:
break;
}
}
}
public void compute(){
Node start = new Node(this.entrance,OperationAndDirection.START,null);
dfs(start,1);
System.out.println(visitedNodes + " nodes were visited.");
System.out.println(solutions + " solutions.");
}
BFS找到了最优解(执行了26次计算)如下:
2011 /2 +7 /2 +7 /2 +7 /2 +7 /2 *3 -5 *3 -5 +7 /2 -5 *3 -5 *3 /2 +7 -5 *3 /2 +7 -5 = 2012
28681973 nodes were visited.
212 solutions.