所有可能的方式来达到结束位置

时间:2014-03-10 09:15:05

标签: java algorithm path catalan

http://www.cstutoringcenter.com/problems/problems.php?id=103

对于那些不想点击它的人,它基本上说有一块踏脚石,“ - ”和士兵“#”,士兵只能向右移动。如果士兵在另一名士兵后面,他必须等待士兵先行动。结束条件是所有士兵到达终点时。

2名士兵可以通过5个踏脚石移动的方式。

1) ##---  #-#--  -##--  -#-#-  --##-  --#-#  ---##
2) ##---  #-#--  -##--  -#-#-  -#--#  --#-#  ---##
3) ##---  #-#--  #--#-  -#-#-  --##-  --#-#  ---##
4) ##---  #-#--  #--#-  -#-#-  -#--#  --#-#  ---##
5) ##---  #-#--  #--#-  #---#  -#--#  --#-#  ---##

我正在使用广度优先搜索,有5块石头,它在几秒钟内运行,但是有10块石头,需要几个小时,时间随着深度呈指数增长。我怎么处理这个?

我的代码:

States.java

import java.util.ArrayList;


public class State {
    public int stones;
    public Soldiers[] soldiers;
    public String currentState =""; 
    public boolean visited = false;

    public State(int stones,int Numsoldiers){
        System.out.println(Numsoldiers);
        this.stones = stones;
        soldiers = new Soldiers[Numsoldiers];
        System.out.println("length" + soldiers.length);
        initState();
    }

    public State(int stones,Soldiers[] soldiers){
        this.stones = stones;
        this.soldiers = soldiers;
        paintState();
    }

    public void initState(){
        for(int i=0;i<soldiers.length;i++)
        {
            soldiers[i] = new Soldiers();
            soldiers[i].position =i;
            currentState+="#";
        }
        for(int j=soldiers.length;j<stones;j++)
        {
            currentState+="-";
        }
    }

    private void paintState(){
        for(int j=0;j<stones;j++)
        {
            currentState+="-";
        }
        char[] stateChar = currentState.toCharArray();
        currentState = "";
        for(int i=0;i<soldiers.length;i++){
            stateChar[soldiers[i].position] = '#';
        }
        for(int k=0; k<stateChar.length;k++){
            currentState += stateChar[k];
        }
    }

    public void printState(){
        System.out.println(currentState);
    }
    public ArrayList<State> getNextStates(){
        ArrayList<State> States = new ArrayList<State>();

        for(int i=0;i<soldiers.length;i++){
            Soldiers[] newSoldiers = new Soldiers[soldiers.length];
            for(int j=0;j<soldiers.length;j++){
                newSoldiers[j] = new Soldiers(soldiers[j].position);
            }
            if(!((newSoldiers[i].position+1)==stones))
            {
                if((currentState.charAt((newSoldiers[i].position+1))=='-'))
                {
                newSoldiers[i].move();
                States.add(new State(stones,newSoldiers));
                }
            }

        }
        if(States.size()==0)
        {
            TestSoldiers.count++;
        }
        return States;
    }

}

Soldiers.java

public class Soldiers {

    int position = 0;

    public Soldiers(){
        position =0;
    }

    public Soldiers(int pos){
        position = pos;
    }

    public void move(){
        position ++;
    }
}

TestSoldiers.java

import java.util.LinkedList;
import java.util.Queue;


public class TestSoldiers {



    public static int count=0;

    public static void main(String[] args){

        TestSoldiers t = new TestSoldiers();
    }
    public TestSoldiers()
    {
        State s = new State(10,3);
        breadthFirstTraversal(s);
        System.out.println(count);
    }

    public void breadthFirstTraversal(State rootNode){

        Queue<State> q = new LinkedList<State>();
        q.add(rootNode);
        while(!q.isEmpty()){
            State n = (State)q.poll();
            n.printState();
            for(State adj : n.getNextStates()){

                        q.add(adj);

                }

            }
        }



}

我怎样才能这样做,以便我只考虑每个州一次,同时保持结束方式总数的完整性(在TestSoldiers.java中计算)?

对于那些想要修改参数的人来说,它是新的状态(n,k),其中n是宝石的数量,k是士兵的数量。

3 个答案:

答案 0 :(得分:2)

Memoization可能派上用场。

想法是运行深度优先搜索来计算从当前状态到结束的方式的数量,并存储此结果,然后查找已经计算的值,如果该状态重复。< / p>

例如,有2种方法可以从-#-#-到达终点,因此,当我们通过-##--到达时,存储此结果,我们只需查找2当我们通过#--#-到达那里时。

存储这些的最简单(但效率最高)的方法就是拥有:

Map<Pair<Integer (Position1), Integer (Position2)>, Integer (Count)>

更一般地说,您可以将Pair设为List

更有效的方法是使用位图,其中每个位对应于某个给定位置是否有士兵。因此,-#-#-将与01010对应,int可以简单地存储在10中作为long的十进制 - 如果有超过64个宝石(即什么适合于{{1}}),您可以使用BitSet

答案 1 :(得分:2)

使用组合学来计算路径数量可能会更好。

例如,假设有2名士兵和5个步骤。

表示第一个士兵移动了y的距离,以及第二个士兵移动了x的距离。

你试图计算从0,0到3,3的单调路径的数量,使得y永远不会大于x。

这是一个众所周知的问题,答案由Catalan numbers给出。在这种情况下,答案由加泰罗尼亚数给出,n = 3,即5。

当你有超过2名士兵时,你需要使用多维加泰罗尼亚数字。可以在OEIS上找到有用的指南和公式:

  

T(m,n)= 0! * 1! * .. *(n-1)! *(m * n)! /(m!*(m + 1)!* .. *(m + n-1)!)

答案 2 :(得分:0)

我的解决方案在不到1秒的时间内运行10个位置。解决方案快速而且肮脏,但算法是你应该感兴趣的吗?

我的算法的想法是:

  1. 管理一组要计算的路径。从两个士兵都在最左边的位置开始。
  2. 如果要计算的路径集不为空,请选择任何路径并将其从集合中删除。
  3. 如果路径终止(两名士兵都在最正确的位置)打印路径。继续2。
  4. 如果可能的话,通过移动头部士兵来扩展路径并将其放入集合中。
  5. 如果可能的话,通过移动尾兵来扩展路径并将其放入集合中。
  6. 就是这样。

    public static void main(String[] args) {
        List<Node> nodes = Node.newRootNode(10);
        while (!nodes.isEmpty()) {
            Node node = nodes.remove(0);
            if (node.isLeaf()) node.printPath();
            else {
                if (node.headSoldierCanMove()) nodes.add(node.moveHeadSoldier());
                if (node.tailSoldierCanMove()) nodes.add(node.moveTailSoldier());
            }
        }
    }
    
    static final class Node {
    
        static List<Node> newRootNode(final int maxPos) {
            return new ArrayList<Node>() {{
                add(new Node(1, 2, maxPos, ""));
            }};
        }
    
        private final int maxPos;
        private final String path;
        private int tailPos = 1;
        private int headPos = tailPos + 1;
    
        private Node(int tailPos, int headPos, int maxPos, String path) {
            this.maxPos = maxPos;
            this.tailPos = tailPos;
            this.headPos = headPos;
            this.path = addPath(path);
        }
    
        boolean tailSoldierCanMove() {
            return tailPos < headPos - 1;
        }
    
        Node moveTailSoldier() {
            return new Node(tailPos + 1, headPos, maxPos, path);
        }
    
        boolean headSoldierCanMove() {
            return headPos < maxPos;
        }
    
        Node moveHeadSoldier() {
            return new Node(tailPos, headPos + 1, maxPos, path);
        }
    
        void printPath() {
            System.out.println(path);
        }
    
        boolean isLeaf() {
            return headPos == maxPos && tailPos == headPos - 1;
        }
    
        private String addPath(String prefix) {
            StringBuilder builder = new StringBuilder(prefix);
            for (int pos = 1; pos <= maxPos; pos++) {
                builder.append(tailPos == pos || headPos == pos ? "#" : "-");
            }
            return builder.append("  ").toString();
        }
    }