在XY图中查找蛇序列 - Java

时间:2014-08-02 12:47:31

标签: java algorithm recursion

我正在研究一个问题,试图在典型的XY图(也就是网格)中找到所谓的Snake序列。 Snake序列被定义为一个数字序列,其中每个新数字(只能位于当前数字的右侧或下方)是加号或减号。例如,如果您位于图表的中心,则可以向右移动(如果该数字为+或 - 1)或向下移动(如果该数字为+或 - 1)。该问题的目标是通过图形找到最长的路径(也称为蛇序列)(请记住,您只能绘制到当前单元格值为+ - 1的新单元格的路径。)

因此,对于以下XY图,最长的蛇序列为:9, 8, 7, 6, 5, 6, 7

9, 6, 5, 2
8, 7, 6, 5
7, 3, 1, 6
1, 1, 1, 7

以下是我的代码,似乎无效。

问题:您如何解决上述问题? (我提供的代码显示了我到目前为止的内容,但它不起作用)

import java.util.ArrayList;

public class SnakeSequence {
  private final int maxX = 3;
  private final int maxY = 3;
  private final int[][] board = new int[][]{
      {1, 2, 3, 4},
      {2, 1, -1, 5},
      {3, 0, -1, 6},
      {6, 2, 1, 7}
  };

  private ArrayList<Integer> findSequence(int xPos,
      int yPos, ArrayList<Integer> currentPath) {
    currentPath.add(board[yPos][xPos]);

    ArrayList<Integer> pathRight = new ArrayList<Integer>(currentPath);
    ArrayList<Integer> pathDown = new ArrayList<Integer>(currentPath);

    if (xPos < maxX || yPos < maxY) {
      if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
                          board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
        pathDown = findSequence(xPos, yPos + 1, currentPath);
      }
      if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
                          board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
        pathRight = findSequence(xPos + 1, yPos, currentPath);
      }

      if (pathDown.size() > pathRight.size()) {
        return pathDown;
      } else {
        return pathRight;
      }
    }
    return currentPath;
  }

  private void getSequence() {
    ArrayList<Integer> currentPath = new ArrayList<Integer>();
    ArrayList<Integer> result;
    result = findSequence(0, 0, currentPath);

    for (int i = 0; i < result.size(); i++) {
      System.out.println(result.get(i));
    }
  }

  public static void main(String[] args) {
    SnakeSequence sequence = new SnakeSequence();
    sequence.getSequence();
  }
}

4 个答案:

答案 0 :(得分:2)

您可以将表格想象为oriented graph,那么您的问题就是找到longest path

幸运的是,只允许向下和向右移动,因此您的图表为acyclic,因此您可以使用critical path method等算法。

这就是图表的样子:

graph image

但是,您希望找到任何两个单元格之间的最长路径。为此,我将为每个单元格计算从该单元格开始的最长路径。它与您的行为相似,但您计算的次数更多。考虑一下:

6 -> 5
|    |
v    v
7 -> 6

同时57计算从6向右下方的路径有多长,这是无用的重复计算。在最坏的情况下,这可能导致指数时间消耗,而问题可以在线性时间内解决!

此外,无法保证最长路径将从(0,0)开始。

(一种可能)解决方案:

计算每个单元格的最长路径,从右下角到左上角。在每个细胞处......记住那个细胞最长的路径有多长,并且从那个细胞开始走路。 (我将根据它上面的细胞数来测量路径长度)。例如,对于您的格鲁吉亚唯一的8,我们会记住[length=8, direction=right]

为什么如此复杂?因为如果我们知道细胞向右和向下的最长路径,现在在单元格中计算最长路径非常容易。示例(我编写):

longest path choosing

现在2的正确数据为[length=4, direction=down],因为无法从2转到4

您还可以保持全球最长的路径并启动它。计算完成后,只需从那个开始通过direction走最长路径并写下数字,位置或任何你需要的路径。

答案 1 :(得分:1)

为我的Java道歉(我主要是一个c#程序员),但这是一个解决方案。我将从算法中发现蛇的算法(实现接口ISnakeProcessor)分离出来,处理每个蛇。通过这种方式,您可以增强代码,例如,使用最大值的值来收集蛇,或者通过添加更多的ISnakeProcessor类来收集所有最长的蛇,以防有多个蛇。

import java.util.*;
import java.lang.*;

class Rextester
{  
    public static void main(String args[])
    {
        SnakeSequence sequence = new SnakeSequence();
        sequence.getSequence();
    }
}

interface ISnakeProcessor
{
    void process(List<Pair<Integer, Integer>> snake);
}

class SnakeSequence {

    private final int[][] board;

    public SnakeSequence()
    {
        this(new int[][]{
            {1, 2, 3, 4},
            {2, 1, -1, 5},
            {3, 0, -1, 6},
            {6, 2, 1, 7}
        });
    }

    public SnakeSequence(int[][] board)
    {
        this.board = board;
    }

    public boolean isValid(int iRow, int iCol)
    {
        if (iRow < 0 || iRow >= board.length)
            return false;
        if (iCol < 0 || iCol >= board[iRow].length)
            return false;
        return true;
    }

    private boolean continuesInRow(int iRow, int iCol)
    {
        if (!isValid(iRow, iCol) || !isValid(iRow+1, iCol))
            return false;
        int myVal = board[iRow][iCol];
        if (board[iRow+1][iCol] == myVal - 1 || board[iRow+1][iCol] == myVal + 1)
            return true;
        return false;
    }

    private boolean continuesInCol(int iRow, int iCol)
    {
        if (!isValid(iRow, iCol) || !isValid(iRow, iCol+1))
            return false;
        int myVal = board[iRow][iCol];
        if (board[iRow][iCol+1] == myVal - 1 || board[iRow][iCol+1] == myVal + 1)
            return true;
        return false;
    }

    private boolean isHead(int iRow, int iCol)
    {
        if (!isValid(iRow, iCol))
            return false;
        if (isValid(iRow-1, iCol) && continuesInRow(iRow-1, iCol))
            return false;
        if (isValid(iRow, iCol-1) && continuesInRow(iRow, iCol-1))
            return false;
        return true;            
    }

    private boolean isTail(int iRow, int iCol)
    {
        if (!isValid(iRow, iCol))
            return false;
        if (continuesInRow(iRow, iCol))
            return false;
        if (continuesInCol(iRow, iCol))
            return false;
        return true;
    }        

    private void testHead() 
    {
        System.out.println("Dumping list of heads");
        for (int iRow = 0; iRow < board.length; iRow++) 
        {
            for (int iCol = 0; iCol < board[iRow].length; iCol++) 
            {
                boolean head = isHead(iRow, iCol);
                boolean tail = isTail(iRow, iCol);
                if (head && tail)
                    System.out.print("  B");
                else if (head)
                    System.out.print("  H");
                else if (tail)
                    System.out.print("  T");
                else
                    System.out.print("  -");
            }
            System.out.println("");
        }
    }

    private void walkSnake(ISnakeProcessor processor, int iRow, int iCol, ArrayList<Pair<Integer, Integer>> snake)
    {
        snake.add(new Pair<Integer, Integer>(iRow, iCol));

        boolean isTail = true;
        if (continuesInRow(iRow, iCol))
        {
            walkSnake(processor, iRow+1, iCol, snake);
            isTail = false;
        }
        if (continuesInCol(iRow, iCol)) 
        {
            walkSnake(processor, iRow, iCol+1, snake);
            isTail = false;
        }
        if (isTail)
        {
            processor.process(snake);
        }
        snake.remove(snake.size() - 1);
    }

    private void walkSnakes(ISnakeProcessor processor)
    {
        ArrayList<Pair<Integer, Integer>> snake = new ArrayList<Pair<Integer, Integer>>();
        for (int iRow = 0; iRow < board.length; iRow++) 
            for (int iCol = 0; iCol < board[iRow].length; iCol++)
                if (isHead(iRow, iCol))
                    walkSnake(processor, iRow, iCol, snake);                
    }

    class LongestSnakeFinder implements ISnakeProcessor
    {
        private final SnakeSequence parent;
        ArrayList<Pair<Integer, Integer>> longest = new ArrayList<Pair<Integer, Integer>>();

        public LongestSnakeFinder(SnakeSequence parent) 
        {
            this.parent = parent;
        }

        public void process(List<Pair<Integer, Integer>> snake)
        {
            if (snake.size() > longest.size())
            {
                longest.clear();
                longest.addAll(snake);
            }
        }

        public void dumpLongest()
        {
            System.out.format("The first encountered longest snake has length %d:\n", longest.size());
            for (int i = 0; i < longest.size(); i++)
            {
                int iRow = longest.get(i).getFirst();
                int iCol = longest.get(i).getSecond();
                System.out.format("   (%d,%d): %d\n", iRow, iCol, parent.getValue(iRow, iCol));
            }
        }
    }

    public int getNRows() { return board.length; }

    public int getNCols(int iRow) { return board[iRow].length; }

    public int getValue(int iRow, int iCol) { return board[iRow][iCol]; }

    public void getSequence() {
        testHead();
        LongestSnakeFinder finder = new LongestSnakeFinder(this);
        walkSnakes(finder);
        finder.dumpLongest();
    }
}

class Pair<F, S> {
    private F first; //first member of pair
    private S second; //second member of pair

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }

    public F getFirst() {
        return first;
    }

    public S getSecond() {
        return second;
    }
}

此处运行示例:http://rextester.com/AKUFNL43897 更新 - 清理了一点代码。新示例在此处运行:http://rextester.com/AVOAIY11573

而且,输出:

Dumping list of heads
  H  -  -  -
  -  -  B  -
  T  -  T  -
  B  H  T  T
The first encountered longest snake has length 7:
   (0,0): 1
   (0,1): 2
   (0,2): 3
   (0,3): 4
   (1,3): 5
   (2,3): 6
   (3,3): 7

这是你想要的吗?

答案 2 :(得分:0)

这是一种纠正解决方案并避免在每一步中复制路径的简单方法

import java.util.ArrayList;
import java.util.Collections;

public class SnakeSequence {
  private final int maxX = 3;
  private final int maxY = 3;
  private final int[][] board = new int[][]{
      {1, 2, 3, 4},
      {2, 1, -1, 5},
      {3, 0, -1, 6},
      {6, 2, 1, 7}
  };

  private ArrayList<Integer> findSequence(int xPos,
      int yPos) {    

      ArrayList<Integer> pathRight = new ArrayList<Integer>();
      ArrayList<Integer> pathDown = new ArrayList<Integer>();    

      if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
                          board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
        pathDown = findSequence(xPos, yPos + 1);
      }
      if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
                          board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
        pathRight = findSequence(xPos + 1, yPos);
      }
      ArrayList<Integer> ans; 
      if (pathDown.size() > pathRight.size()) {
          ans = pathDown;
      } else {
          ans = pathRight;
      }
      ans.add(board[yPos][xPos]);
      return ans;    
  }

  private void getSequence() {
     ArrayList<Integer> result;
     result = findSequence(0, 0);
     Collections.reverse(result);
     for (int i = 0; i < result.size(); i++) {
       System.out.println(result.get(i));
    }
  }

  public static void main(String[] args) {
    SnakeSequence sequence = new SnakeSequence();
    sequence.getSequence();

  }
}

但是这样,对于大型数组,它可以更快地工作,因为每次在递归期间访问相同的数字时都不会重新计算最长的路径。实际上,在这个版本中,每个号码最多访问两次。它通过为每个节点保存最佳解决方案来实现。单独存储路径和长度不允许在不需要时复制路径。

import java.util.ArrayList;
import java.util.Collections;

public class SnakeSequence {
  private final int maxX = 3;
  private final int maxY = 3;
  private final int[][] board = new int[][]{
      {1, 2, 3, 4},
      {2, 3, -1, 5},
      {3, 2, -1, 6},
      {6, 1, 2, 3}
  };    
  int[][] pathLength;
  ArrayList<ArrayList<ArrayList<Integer>>> paths;

  private ArrayList<Integer> findSequence(int xPos,
      int yPos) { 
      if(pathLength[yPos][xPos] >= 0)
      {
          ArrayList<Integer> ans = new ArrayList<Integer>();
          int length =  pathLength[yPos][xPos];
          ArrayList<Integer> path = paths.get(yPos).get(xPos);
          for(int i = 0; i < length; i++)
              ans.add(path.get(i));
          return ans;
      }

      ArrayList<Integer> pathRight = new ArrayList<Integer>();
      ArrayList<Integer> pathDown = new ArrayList<Integer>();

      if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
                          board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
        pathDown = findSequence(xPos, yPos + 1);
      }
      if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
                          board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
        pathRight = findSequence(xPos + 1, yPos);
      }
      ArrayList<Integer> ans; 
      if (pathDown.size() > pathRight.size()) {
          ans = pathDown;
      } else {
          ans = pathRight;
      }
      ans.add(board[yPos][xPos]);      
      paths.get(yPos).set(xPos,ans);
      pathLength[yPos][xPos] = ans.size();
      return ans;    
  }

  private void getSequence() {
     ArrayList<Integer> result;
     pathLength = new int[maxX + 1][maxY + 1];
     paths = new ArrayList<ArrayList<ArrayList<Integer>>>();
     for(int y = 0; y <= maxY; y++)
     {
         ArrayList<ArrayList<Integer>> line = new ArrayList<ArrayList<Integer>>();       
         for(int x = 0; x <= maxX; x++)
         {
             line.add(null);
             pathLength[y][x] = -1;
         }
         paths.add(line);
     }
     result = findSequence(0, 0);
     Collections.reverse(result);
     for (int i = 0; i < result.size(); i++) {
       System.out.println(result.get(i));
    }
  }

  public static void main(String[] args) {
    SnakeSequence sequence = new SnakeSequence();
    sequence.getSequence();        
  }
}

答案 3 :(得分:0)

简单递归解决方案:

import java.util.ArrayList;
import java.util.List;

public class MaximumLengthSnakeSequence {

    static int max = -1;
    static List<Integer> maxListTemp = new ArrayList<>();

    public static void main(String args[]) {
        int count = 0;
        int n = 4;
        int m = 4;

        int mat[][] = { { 9, 6, 5, 2 }, { 8, 7, 6, 5 }, { 7, 3, 1, 6 }, { 1, 1, 1, 7 }, };
        List<Integer> maxList = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                List<Integer> list = new ArrayList<>();
                list.add(mat[i][j]);
                List<Integer> testList = recur(i, j, count, mat, n, m, list);
                if (maxList.size() < testList.size()) {
                    maxList = new ArrayList<>(testList);
                }
                maxListTemp.clear();
            }
        }

            System.out.println("max is " + maxList);
    }

    static List<Integer> recur(int i, int j, int count, int mat[][], int n, int m, List<Integer> list) {
        int curData = mat[i][j];
        int rightData = 0;
        int downData = 0;
        if (j + 1 < n && i < m) {
            rightData = mat[i][j + 1];
            if (Math.abs(curData - rightData) == 1) {
                list.add(rightData);
                recur(i, j + 1, count + 1, mat, n, m, list);
                list.remove(list.size() - 1);
            }
        }

        if (count > max) {
            max = count;
        }
        if (maxListTemp.size() < list.size()) {
            maxListTemp = new ArrayList<>(list);
        }

        if (i + 1 < m && j < n) {
            downData = mat[i + 1][j];
            if (Math.abs(curData - downData) == 1) {
                list.add(downData);
                recur(i + 1, j, count + 1, mat, n, m, list);
                list.remove(list.size() - 1);
            }
        }

        return maxListTemp;
    }
}