Function to see if there is a break in circuit (2D Array) - java

I want to create a function that lets me see if there is a break in a circuit which I've stored in a 2D Array by returning either true or false. For simplicity sake i have modeled the circuit using 1's and 0's. With 1's being components and 0's being spaces. I have tried messing with recursive functions however to no avail and am pretty much stuck at this point.
For example:
1 1 1
1 0 1
1 1 1
I would like this to return true as all the 1's are connected in series. This 2D array would be visualized as seen here.
1 1 1
0 0 1
1 1 1
I would like this to return false as there is a break in the circuit as
as shown here.
Any solutions or guidance would be much appreciated!
My current code is shown below. It returns incomplete when I used the completeCircuit as an input however returns complete when I have the incompleteCircuit as an input.
public class TraversalTest
{
boolean complete = false;
int runs = 0;
public static void main(String[] args)
{
TraversalTest traversal = new TraversalTest();
}
public TraversalTest()
{
Cell[][] completeCircuit =
{
{ new Cell( 0, 0, 1), new Cell( 1, 0, 1), new Cell( 2, 0, 1) },
{ new Cell( 0, 1, 1), new Cell( 1, 1, 0), new Cell( 2, 1, 1) },
{ new Cell( 0, 2, 1), new Cell( 1, 2, 1), new Cell( 2, 2, 1) }
};
Cell[][] incompleteCircuit =
{
{ new Cell( 0, 0, 1), new Cell( 1, 0, 1), new Cell( 2, 0, 1) },
{ new Cell( 0, 1, 0), new Cell( 1, 1, 0), new Cell( 2, 1, 1) },
{ new Cell( 0, 2, 1), new Cell( 1, 2, 1), new Cell( 2, 2, 1) }
};
completeCircuit[1][0].connected = true;
int cellsLeft = (numOfPositiveCells(completeCircuit));
checkComplete(completeCircuit, completeCircuit[1][0], cellsLeft);
incompleteCircuit[1][0].connected = true;
int cellsLeft1 = (numOfPositiveCells(incompleteCircuit));
checkComplete(incompleteCircuit, incompleteCircuit[1][0], cellsLeft1);
}
void checkComplete(Cell[][] circuit, Cell currentCell, int cellsLeft)
{
currentCell.connected = true;
if(cellsLeft > 0)
{
if(currentCell.x != 0 && circuit[currentCell.x-1][currentCell.y].value == 1 &&
circuit[currentCell.x-1][currentCell.y].connected == false)
{
cellsLeft--;
checkComplete(circuit, circuit[currentCell.x-1][currentCell.y], cellsLeft);
}
else if(currentCell.x != 2 &&circuit[currentCell.x+1][currentCell.y].value == 1 &&
circuit[currentCell.x+1][currentCell.y].connected == false)
{
cellsLeft--;
checkComplete(circuit, circuit[currentCell.x+1][currentCell.y], cellsLeft);
}
else if(currentCell.y != 0 && circuit[currentCell.x][currentCell.y-1].value == 1 &&
circuit[currentCell.x][currentCell.y-1].connected == false)
{
cellsLeft--;
checkComplete(circuit, circuit[currentCell.x][currentCell.y-1], cellsLeft);
}
else if(currentCell.y != 2 && circuit[currentCell.x][currentCell.y+1].value == 1 &&
circuit[currentCell.x][currentCell.y+1].connected == false)
{
cellsLeft--;
checkComplete(circuit, circuit[currentCell.x][currentCell.y+1], cellsLeft);
}
else
{
complete = false;
System.out.println("Incomplete");
}
}
else
{
complete = true;
System.out.println("Complete");
}
}
int numOfPositiveCells(Cell[][] circuit)
{
int num = 0;
for(int x=0; x < 3; x++)
for(int y=0; y < 3; y++)
if(circuit[x][y].value == 1)
num++;
return num;
}
}
class Cell
{
public boolean connected;
public int value;
public int x;
public int y;
public Cell(int x, int y, int value)
{
this.x = x;
this.y = y;
this.value = value;
}
}

I think checking that every cell has 2 connections should work for your use cases as we only look for series connections.
You just need to go through your array and make sure that all live cells have 2 and only 2 live cells connected to it
I'd suggest you make a dedicated class, this way you don't have to keep passing the circuit around in all the methods:
class CircuitChecker {
private final Cell[][] circuit;
private final int nbRows, nbCols;
public CircuitChecker(Cell[][] circuit) {
this.circuit = circuit;
this.nbRows = circuit.length;
this.nbCols = circuit[0].length;
}
public boolean isCircuitComplete() {
boolean isComplete = true;
for(int col = 0; col<nbCols; col++) {
for (int row = 0; row < nbRows; row++) {
if(cellIsLive(col, row) && !cellHas2LiveConnections(col, row)) {
isComplete = false;
break;
}
}
}
return isComplete;
}
private boolean cellIsLive(int col, int row) {
return circuit[row][col].value == 1;
}
private boolean cellHas2LiveConnections(int col, int row) {
Cell left = col > 0 ? circuit[col-1][row] : null;
Cell right = col < nbCols-1 ? circuit[col+1][row] : null;
Cell up = row > 0 ? circuit[col][row-1] : null;
Cell down = row < nbRows-1 ? circuit[col][row+1] : null;
int nbConnections = Stream.of(left, right, up, down)
.filter(Objects::nonNull)
.mapToInt(c -> c.value)
.sum();
return nbConnections == 2;
}
}
you call it this way:
new CircuitChecker(completeCircuit).isCircuitComplete();
new CircuitChecker(incompleteCircuit).isCircuitComplete();
One more thing, the fields in your class Cell should be private (and even maybe final) and you should access them via getters.

Related

Finding the number of areas of true values in a 2D array

I have a question about my homework in scratching my head-on.
So I need to build a program that counts the number of areas of true value (near to each other) in the 2D array.
{0,1,0,0}
{1,0,0,1}
{1,1,0,1}
{1,0,0,1}
So the program needs to return 3 because there 3 places with trues.
If there are no true then to return 0.
The program needs to recursive and without loops.
Thanks in advance to the ones that will help me solve this one. I didn't even have any idea how to start it.
Ok, you do a dfs to record the cells you went and you can have a boolean[][] to record the cells you went. Then you just loop through the grid to see where you have not gone. If you did not go there yet, go there and also perform a dfs on nearby cells, but anyway, a code would be more clear.
public static int[][] grid;
public static boolean[][] went;
public static int r, c;
public static void dfs(int x, int y) {
if (x < 0 || x >= r || y < 0 || y >= c) {//out of the grid
return;
}
if (went[x][y]) {//I went here
return;
}
went[x][y] = true;
if (grid[x][y] == 0) {//It's false here so don't go
return;
}
dfs(x - 1, y);
dfs(x + 1, y);
dfs(x, y - 1);
dfs(x, y + 1);
}
public static void main(String[] args) throws IOException {
BufferedReader input =
new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(input.readLine());
//input reading
r = Integer.parseInt(st.nextToken());
c = Integer.parseInt(st.nextToken());
grid = new int[r][c];
went = new boolean[r][c];
for (int i = 0; i < r; i++) {
st = new StringTokenizer(input.readLine());
for (int j = 0; j < c; j++) {
grid[i][j] = Integer.parseInt(st.nextToken());
}
}
//end of input reading
int cnt = 0;//number of areas
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
// If I have not went have and the value
// must be true so I would perform a dfs
// (since I only care about the value of 1s)
if (!went[i][j] && grid[i][j] == 1) {
cnt++;//add to our counter
dfs(i, j);//have not went here
}
}
}
System.out.println(cnt);
}
This solution uses the only recursion. No loops.
public static void main(String... args) {
System.out.println(countTrueAreas(new int[][] {
{ 0, 1, 0, 0 },
{ 1, 0, 0, 1 },
{ 1, 1, 0, 1 },
{ 1, 0, 0, 1 } })); // 3
}
public static int countTrueAreas(int[][] grid) {
return countTrueAreas(grid, 0, 0, 10) - 10;
}
private static int countTrueAreas(int[][] grid, int row, int col, int num) {
if (row == grid.length)
return num;
if (col == grid[row].length) {
if (++row == grid.length)
return num;
col = 0;
}
if (grid[row][col] == 1)
dfs(grid, row, col, num++);
return countTrueAreas(grid, row, col + 1, num);
}
private static void dfs(int[][] grid, int row, int col, int num) {
if (row < 0 || row >= grid.length)
return;
if (col < 0 || col >= grid[row].length)
return;
if (grid[row][col] != 1)
return;
grid[row][col] = num;
dfs(grid, row, col - 1, num);
dfs(grid, row, col + 1, num);
dfs(grid, row - 1, col, num);
dfs(grid, row + 1, col, num);
}

What's wrong with my Tic Tac Toe AI?

I read a tutorial about minimax, and tried to make a tac tac toe AI.
But the code doesn't work optimally for some reason, which I cannot find. The ai can place pieces, but it's not a smart ai. I expected it to be unbeatable. The higher the depth is, the dumber the ai becomes.
The 'game' is my an other class, where the actual game is.
private Game game;
private Piece[][] board;
private Piece ai = Piece.CIRCLE;
private Piece player = Piece.CROSS;
public AI(Game game) {
this.game = game;
this.board = game.getBoard();
}
public int[] move() {
int[] result = minimax(1, ai);
return new int[] {result[1], result[2]};
}
private int[] minimax(int depth, Piece piece) {
List<int[]> possibleMoves = generateMoves();
int bestScore = (piece == ai) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
int currentScore;
int bestRow = -1;
int bestCol = -1;
if (possibleMoves.isEmpty() || depth == 0) {
// Game over or depth reached
bestScore = evaluate();
}
else {
for (int[] move : possibleMoves) {
// Try this move for the player
board[move[0]][move[1]] = player;
if (piece == ai) { // ai is maximizing player
currentScore = minimax(depth - 1, player)[0];
if (currentScore > bestScore) {
bestScore = currentScore;
bestRow = move[0];
bestCol = move[1];
}
}
else { // player is minimizing player
currentScore = minimax(depth - 1, ai)[0];
if (currentScore < bestScore) {
bestScore = currentScore;
bestRow = move[0];
bestCol = move[1];
}
}
// Undo move
board[move[0]][move[1]] = null;
}
}
return new int[] {bestScore, bestRow, bestCol};
}
private List<int[]> generateMoves() {
List<int[]> possibleMoves = new ArrayList<int[]>();
// If game over
if (game.getWinner() != null) {
return possibleMoves; // return empty list
}
// Add possible moves to list
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
if (game.getBoard()[x][y] == null) {
possibleMoves.add(new int[] {x, y});
}
}
}
return possibleMoves;
}
private int evaluate() {
int score = 0;
// Evaluate
score += evaluateLine(0, 0, 0, 1, 0, 2); // row 0
score += evaluateLine(1, 0, 1, 1, 1, 2); // row 1
score += evaluateLine(2, 0, 2, 1, 2, 2); // row 2
score += evaluateLine(0, 0, 1, 0, 2, 0); // col 0
score += evaluateLine(0, 1, 1, 1, 2, 1); // col 0
score += evaluateLine(0, 2, 1, 2, 2, 2); // col 0
score += evaluateLine(0, 0, 1, 1, 2, 2); // diag 1
score += evaluateLine(0, 2, 1, 1, 2, 0); // diag 2
return score;
}
// Return +100, +10, +1 for 3-, 2-, 1-in-a-line for ai
// Return -100, -10, -1 for 3-, 2-, 1-in a line for player
// Else return 0
private int evaluateLine(int row1, int col1, int row2, int col2, int row3, int col3) {
int score = 0;
// First cell
if (board[row1][col1] == ai) {
score = 1;
}
else if (board[row1][col1] == player) {
score = -1;
}
// Second cell
if (board[row2][col2] == ai) {
if (score == 1) { // board1 is ai
score = 10;
}
else if (score == -1) { // board1 is player
return 0;
}
else { // board1 is empty
score = 1;
}
}
else if (board[row2][col2] == player) {
if (score == -1) { // board1 is player
score = -10;
}
else if (score == 1) { // board1 is ai
return 0;
}
else { // board1 is empty
score = -1;
}
}
// Third cell
if (board[row3][col3] == ai) {
if (score > 0) { // board1 and/or board2 is ai
score *= 10;
}
else if (score < 0) { // board1 and/or board2 is player
return 0;
}
else { // board1 and/or board2 is empty
score = 1;
}
}
else if (board[row3][col3] == player) {
if (score < 0) { // board1 and/or board2 is player
score *= 10;
}
else if (score > 1) { // board1 and/or board2 is ai
return 0;
}
else { // board1 and/or board2 is empty
score = -1;
}
}
return score;
}
A couple of things I noticed:
The first line in the loop going through possible moves says board[move[0]][move[1]] = player;. That should be piece instead of player, now your AI thinks that only pieces of the human player ever end up on the board.
Minimax should be very easily capable of searching the complete game tree in less than a second. Therefore, I'd recommend allowing to to search as deep as it likes, instead of limiting to a search depth of 1. This would also eliminate the need for creating that heuristic evaluation function; you'd only give a large score for winning, 0 for tie, and a very negative score for losing. The main reason I'm recommending this is that I suspect there may be something wrong with the evaluation function too, though I'm not sure since I did not check it in detail. If you really do insist on terminating the search early and using a heuristic evaluation function, you need to make sure that the function is ''symmetrical''. With that, I mean that evaluating the board from the perspective of one player should always result in exactly -1 times the score of the same board were evaluated from the perspective of the opponent.
minimax is returning a move in terms of a row/column pair, not a score. So
currentScore = minimax(depth - 1, player)[0];
makes no sense. It probably causes any move to row 3 to look better than any move to row 1 or row 2.
minmax needs to hand
back a score in addition to the best move.

finding path from tof left to bottom right in 2d array of 1's and 0's

I have 2-D array of 1 and 0's.where top left corner and bottom right corner is filled with 1 and rest of index is filled with 0 and 1.
a[][]= {{1,1,0,0,1}{0,1,1,0,0}{0,1,0,1,0}{0,1,1,1,1}} .
write a program/algorithm that will return the other 2-D array of same size, contain the path from start to end.
output[][]={{1,1,0,0,0}{0,1,0,0,0}{0,1,0,0,0}{0,1,1,1,1}}.
please help me out for above issue. Also the program should work for other sequence of input.
Maybe I can provide you with a cleaner version of the backtracking algorithm. Her you go:
import java.util.Arrays;
public class Backtrack {
public static void main(String... args) {
int[][] inputArray = {
{ 1, 1, 1, 0, 0 },
{ 0, 1, 1, 0, 0 },
{ 0, 1, 0, 1, 0 },
{ 0, 1, 1, 1, 1 } };
int[][] findPath = findPath(inputArray);
System.out.println(Arrays.deepToString(findPath));
}
public static int[][] findPath(int[][] maze) {
int[][] solution = new int[maze.length][];
for (int i = 0; i < maze.length; i++) {
solution[i] = new int[maze[i].length];
}
if (!findPath(maze, solution, 0, 0)) {
System.out.println("Didn't find a solution.");
}
return solution;
}
private static boolean findPath(int[][] maze, int[][] solution, int x, int y) {
if (0 <= y && y < maze.length && 0 <= x && x < maze[y].length) {
if (y == maze.length - 1 && x == maze[y].length - 1) {
solution[y][x] = 1;
return true;
} else if (solution[y][x] != 1 && maze[y][x] == 1) {
solution[y][x] = 1;
if (findPath(maze, solution, x, y + 1)
|| findPath(maze, solution, x + 1, y)
|| findPath(maze, solution, x - 1, y)
|| findPath(maze, solution, x, y - 1)) {
return true;
}
solution[y][x] = 0;
}
}
return false;
}
}

Time complexity analysis of minimum set cover solution

For the question
There are n persons and k different type of dishes. Each person has
some preference for each dish. Either he likes it or not. We need to
feed all people. Every person should get atleast one dish of his
chioce. What is the minimum number of different type of dishes we can
order?
One of the solution is,
public class OptimumDish {
private Set<Integer> result = new HashSet<Integer>();
public void print(){
for(int r:result)
System.out.print(r + " ");
}
// Find the optimum dish by navigating all available options
public void find(int[][] m, int r, int c, int mr, int mc, Stack<Integer> dishes) {
dishes.push(c);
if (r == mr) {
// Reached last person. Get the unique dishes
Set<Integer> d = new HashSet<>(dishes);
if(result.size() == 0 || result.size() > d.size())
result = d;
}
else if (r < mr) {
// Check next person's preferred dish
for (int i = 0; i <= mc; i++) {
if (m[r + 1][i] == 1) {
find(m, r+1, i, mr, mc, dishes);
break;
}
}
}
dishes.pop();
// Current dish may not be the optimum.
// Check other dish for the same person
for (int i = c + 1; i <= mc; i++) {
if (m[r][i] == 1) {
find(m, r, i, mr, mc, dishes);
}
}
}
public static void main(String[] args) {
int[][] m = {
{ 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 1, 0 },
{ 1, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 1, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 1 }
};
int mr = m.length - 1;
int mc = m[0].length - 1;
int c = 0;
for (int i = 0; i <= mr; i++) {
if (m[0][i] == 1) {
c = i;
break;
}
}
OptimumDish od = new OptimumDish();
Stack<Integer> dishes = new Stack<>();
od.find(m, 0, c, mr, mc, dishes);
od.print();
}
}
This problem is of type 'Minimum Set Cover'. Since it is a NP-Complete problem, it can't be solved in polynomial time. As per the solution it can be solved in polynomial time?
Please let me know what is the time complexity of this solution? O(n^4)?. Thanks.

Finding snake sequence in XY graph - Java

I am working on a problem to try to find what is called a Snake Sequence in a typical XY graph (aka grid). A Snake sequence is defined as a sequence of numbers where each new number, which can only be located to the right or down of the current number, is either plus or minus one. For example, if you are in the center of the graph, you can either move right (if that number is + or - 1) or move down (if that number is + or - 1). The goal of the problem is to find the longest path (aka Snake Sequence) through the graph (keeping in mind you can only chart a path to a new cell whose value is +- 1 with the current cell).
So, for the following XY graph, the longest snake sequence is: 9, 8, 7, 6, 5, 6, 7
9, 6, 5, 2
8, 7, 6, 5
7, 3, 1, 6
1, 1, 1, 7
Below is my code, which does not seem to work.
Question: How would you solve this problem above? (I offer my code showing what I have thus far, but it does not work)
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();
}
}
You can imagine your table as an oriented graph, then you problem is just to find the longest path.
Fortunatly for you, only moving down and right is allowed, so your graph is acyclic, so you can use algorithms like critical path method.
This is how your graph would look like:
However, you want to find longest path between any two cells. To do that, I would compute for each cell the longest path starting at that cell. It is simmilar to what you do, but you compute same thing more times. Consider this:
6 -> 5
| |
v v
7 -> 6
At both 5 and 7 you compute how long is the path from 6 at down right, and that is useless repeated computation. In worst case scenario, this could lead to exponential time consumption, while the problem can be resolved in linear time!
Moreover, there is no guarantee that the longest path will start at (0,0).
(one possible) Solution:
Compute longest path from each cell, starting from bottom-right to upper-left. At each cel.. remember how long the longest path from that cell is, and witch way from that cell the path leads. (I will measure path length by number of cells on it). So for example, for the only 8 in your grapth, we would remeber [length=8, direction=right].
Why so complicated? Because it is now extramly easy to compute longest path at a cell, if we know the longest path of the cells to the right and down. Example (I made up):
The correct data for 2 now would be [length=4, direction=down] because can't go from 2 to 4.
You can also keep globally longest path and it's start. After the computation is complete, just walk the longest path from that start through the direction and write the numbers, position or whatever you need.
Apologies for my Java (I am primarily a c# programmer) but here is one solution. I separated out the algorithm that discovers the snakes from the algorithm (implementing the interface ISnakeProcessor) that processes each one. That way you could enhance the code to, e.g., collect the snake with the largest sum of values, or collect all the longest snakes, in case there are more than one, by adding more ISnakeProcessor classes.
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;
}
}
Example run here: http://rextester.com/AKUFNL43897
Update - cleaned code a little. New sample run here: http://rextester.com/AVOAIY11573
And, the output:
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
Is this what you want?
Here is one simple way to correct your solution and avoid copying of path on every step
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();
}
}
But this way it can work much faster for big arrays due to no recalculating the longest path every time you visiting the same number during recursion. Actually, in this version each number is visited at most twice. It's achieved through saving best solution for every node. Separate storage of path and it length allows not to copy path when it's not needed.
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();
}
}
Simple Recursive solution :
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;
}
}

Categories