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是士兵的数量。
答案 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个位置。解决方案快速而且肮脏,但算法是你应该感兴趣的吗?
我的算法的想法是:
就是这样。
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();
}
}