【问题标题】:All possible ways to reach an ending position到达结束位置的所有可能方式
【发布时间】:2014-03-10 09:15:05
【问题描述】:

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 中计数)?

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

【问题讨论】:

  • 您是否正在尝试找出他们可以采取的方式或有多少种方式?
  • 有多少,就是图

标签: java algorithm path catalan


【解决方案1】:

Memoization 可能会派上用场。

我们的想法是运行深度优先搜索以计算从当前状态到结束的方式数量,并存储此结果,然后在该状态重复时查找已计算的值。

例如,有2 方法可以从-#-#- 到达终点,因此,当我们通过-##-- 到达那里时存储这个结果,当我们通过@987654327 到达那里时,我们可以简单地查找2 @。

存储这些的最简单(但远非最有效)的方法就是拥有一个:

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

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

更有效的方法是使用位图,其中每个位对应于某个给定位置是否有士兵。所以-#-#- 将对应于01010,它可以简单地存储在int 中作为十进制的10 - 如果有超过64 块石头(即适合long 的石头),你可以使用BitSet

【讨论】:

  • 如果我错了,请纠正我,但是我看不到运行深度优先搜索如何计算从当前状态到结束的方式的数量,但是它可以计算所需的回合数。
  • @McKevin 可以使用 BFS 或 DFS 解决问题 - 解决方案可能看起来非常相似,如果您愿意,确实可以使用 BFS - DFS 将探索与 BFS 完全相同的节点,只是顺序不同。我更喜欢 DFS 的原因——它通常允许一个非常简单的递归算法,将 memoization 合并到这个递归算法中应该几乎是微不足道的(因为让它与 BFS 一起工作可能有点任务),并且 DFS 可能使用更少的内存(取决于问题)。
  • 我认为无论是使用 DFS 还是 BFS 强制使用它都会很快变得太慢。
  • @Trengot 我的回答的本质不是使用 DFS 代替 BFS,而是使用 memoization,只使用 DFS 使这样做更容易。记忆不再归类为蛮力(至少在我的书中)并且应该是蛮力解决方案的巨大改进,它是否可以接受的快取决于石头和士兵的数量以及允许的执行时间。
  • @Dukeling,公平点。我仍然认为从组合学的角度来解决这类问题会更​​好。
【解决方案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)!)

【讨论】:

  • 如果您只想要计数,这就是解决方法。只是使用公式来计算这个似乎是 O(n^2 * log(n) * m) 虽然这也不是非常快。 (如果我错了,请有人纠正我,这似乎很慢)
  • @Trengot 我认为在这种情况下它不会太慢,因为 m 和 n 只是两位数。
  • 对于任何实际用途来说都很好,但它让我觉得它比我想象的“只是”将数字插入公式更复杂。这仍然是我会使用的方法。
【解决方案3】:

我的解决方案在 1 秒内运行 10 个位置。解决方案又快又脏,但算法才是你应该感兴趣的吧?

我的算法思路是:

  1. 管理一组计算路径。从两个士兵都在最左边位置的路径开始。
  2. 如果要计算的路径集合不为空,则选择任何路径并将其从集合中移除。
  3. 如果路径终止(两个士兵都在最右边的位置)打印路径。继续 2。
  4. 如果可能的话,通过移动头部士兵来延长路径并将其放入布景中。
  5. 如果可能,通过移动尾兵来延长路径并将其放入布景中。

就是这样。

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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 2020-09-19
    • 1970-01-01
    相关资源
    最近更新 更多