I have looked at this question here I tried most of the code samples from there but when i use it in my code it just skips the algorithm.
I have this DFS algorithm that uses stack, I am getting a EmptyStackException, I have debugged the algorithm and after first recursive search the stack is empty, the first search works but then the size of stack is set to 0, What am I missing here? github
How can I make sure that the stack is not empty after the first search?
The line that I get the exception on is while(true){AddBridges state = gameTree.peek(); ...
I am using a 2d Array to generate the nodes at random from 0 to 4 0 = null 1-4 = island The array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake,
After a weekend of debugging I found that the algorithm sometimes brakes after 4-6 searches, and sometimes breaks after the first search.
public int[][] debug_board_state_easy = new int[4][4];
//This Generates random 2d array
private void InitializeEasy() {
Random rand = new Random();
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
debug_board_state_easy[row][column] = Integer.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
System.out.print(debug_board_state_easy[row][column] + " ");
}
System.out.println(debug_board_state_easy);
}
//I am applying the search algorithm here...
this.search();
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.valueOf(debug_board_state_easy[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
void search() {
Map<Point, List<Direction>> remainingOptions = new HashMap<>();
Stack<Land> gameTree = new Stack<>();
gameTree.push(new AddBridges(debug_board_state_easy));
while(true) {
AddBridges state = gameTree.peek();
int[] p = state.lowestTodo();
if (p == null)
System.out.println("solution found");
// move to next game state
int row = p[0];
int column = p[1];
System.out.println("expanding game state for node at (" + row + ", " + column + ")");
List<Direction> ds = null;
if(remainingOptions.containsKey(new Point(row,column)))
ds = remainingOptions.get(new Point(row,column));
else{
ds = new ArrayList<>();
for(Direction dir : Direction.values()) {
int[] tmp = state.nextIsland(row, column, dir);
if(tmp == null)
continue;
if(state.canBuildBridge(row,column,tmp[0], tmp[1]))
ds.add(dir);
}
remainingOptions.put(new Point(row,column), ds);
}
// if the node can no longer be expanded, and backtracking is not possible we quit
if(ds.isEmpty() && gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
Direction dir = ds.remove(0);
System.out.println("connecting " + dir.name());
remainingOptions.put(new Point(row,column), ds);
AddBridgesnextState = new AddBridges(state);
int[] tmp = state.nextIsland(row,column,dir);
nextState.connect(row,column, tmp[0], tmp[1]);
gameTree.push(nextState);
}
}
}
Add bridges class
public class AddBridges {
private int[][] BRIDGES_TO_BUILD;
private boolean[][] IS_ISLAND;
private Direction[][] BRIDGES_ALREADY_BUILT;
public Land(int[][] bridgesToDo){
BRIDGES_TO_BUILD = copy(bridgesToDo);
int numberRows = bridgesToDo.length;
int numberColumns = bridgesToDo[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = null;
IS_ISLAND[i][j] = bridgesToDo[i][j] > 0;
}
}
}
public AddBridges (AddBridges other){
BRIDGES_TO_BUILD = copy(other.BRIDGES_TO_BUILD);
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = other.BRIDGES_ALREADY_BUILT[i][j];
IS_ISLAND[i][j] = other.IS_ISLAND[i][j];
}
}
}
public int[] next(int r, int c, Direction dir){
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// motion vectors
int[][] motionVector = {{-1, 0},{0,1},{1,0},{0,-1}};
int i = Arrays.asList(Direction.values()).indexOf(dir);
// calculate next
int[] out = new int[]{r + motionVector[i][0], c + motionVector[i][1]};
r = out[0];
c = out[1];
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// return
return out;
}
public int[] nextIsland(int row, int column, Direction dir){
int[] tmp = next(row,column,dir);
if(tmp == null)
return null;
while(!IS_ISLAND[tmp[0]][tmp[1]]){
tmp = next(tmp[0], tmp[1], dir);
if(tmp == null)
return null;
}
return tmp;
}
public boolean canBuildBridge(int row0, int column0, int row1, int column1){
if(row0 == row1 && column0 > column1){
return canBuildBridge(row0, column1, row1, column0);
}
if(column0 == column1 && row0 > row1){
return canBuildBridge(row1, column0, row0, column1);
}
if(row0 == row1){
int[] tmp = nextIsland(row0, column0, Direction.EAST);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0)
return false;
if(BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = column0; i <= column1 ; i++) {
if(IS_ISLAND[row0][i])
continue;
if(BRIDGES_ALREADY_BUILT[row0][i] == Direction.NORTH)
return false;
}
}
if(column0 == column1){
int[] tmp = nextIsland(row0, column0, Direction.SOUTH);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0 || BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = row0; i <= row1 ; i++) {
if(IS_ISLAND[i][column0])
continue;
if(BRIDGES_ALREADY_BUILT[i][column0] == Direction.EAST)
return false;
}
}
// default
return true;
}
public int[] lowestTodo(){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
int[] out = {0, 0};
for (int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
if(BRIDGES_TO_BUILD[i][j] == 0)
continue;
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0)
out = new int[]{i, j};
if (BRIDGES_TO_BUILD[i][j] < BRIDGES_TO_BUILD[out[0]][out[1]])
out = new int[]{i, j};
}
}
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0) {
return null;
}
return out;
}
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int[][] copy(int[][] other){
int[][] out = new int[other.length][other.length == 0 ? 0 : other[0].length];
for(int r=0;r<other.length;r++)
out[r] = Arrays.copyOf(other[r], other[r].length);
return out;
}
public void connect(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
connect(r0, c1, r1, c0);
return;
}
if(c0 == c1 && r0 > r1){
connect(r1, c0, r0, c1);
return;
}
if(!canBuildBridge(r0, c0, r1, c1))
return;
BRIDGES_TO_BUILD[r0][c0]--;
BRIDGES_TO_BUILD[r1][c1]--;
if(r0 == r1){
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
BRIDGES_ALREADY_BUILT[r0][i] = Direction.EAST;
}
}
if(c0 == c1){
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
BRIDGES_ALREADY_BUILT[i][c0] = Direction.NORTH;
}
}
}
}
One part of your question stood out to me as the root of the problem: "What am I missing here"? Unit tests, unless I just didn't see them in your project.
Questions like "the array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake?", are the reason unit tests exist, along with the following:
In the course of writing tests on sections of code that don't end up
being the problem, you'll prove definitively that they aren't the
problem.
If the code you're working with can't be tested as-is, or
is "too complex" to test, re-writing it will make you a better
designer and will often lead to "I can't believe I didn't see that"
moments.
When you refactor this program (reduce complexity, rename variables to make them easier to understand, etc), you'll be notified immediately if something breaks
and you won't have to spend another weekend trying to figure it out.
As an example, instead of randomizing the board within the method that searches it, randomize it elsewhere and then drop it into that method as an argument (or into the class' constructor). That way, you can initialize your own test board(s) with your own supplied values and see if some boards work better than others and why. Split up larger methods into smaller methods, each with their own parameters and tests. Aim to make a program out of smaller confirmed-working pieces, instead of making a huge thing and then wondering if the problem is the small part you think it is or something else.
You'll save so much time and frustration in the long run, and you'll end up leagues ahead of those who code for hours and then debug for hours.
There's a lot going on in the code, but the first thing I notice might help:
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
you pop from the stack, and continue onto the next while(true) iteration, at that point, there may be nothing on the stack since you have not added anything else.
I agree with #Rosa -
The EmptyStackException should occur when removing or looking up empty Stack-
======Iteration/State in code ======
**if(ds.isEmpty()){** //HashMap isEmpty = true and gameTree.size() = 1
gameTree.pop(); // After removing element gameTree.size() = 0 (no elements in stack to peek or pop)
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue; //Skip all the instruction below this, continue to next iteration
}
========Next iteration========
while(true){
AddBridges state = gameTree.peek(); // gameTree.size() = 0 and a peek
operation shall fail and program will return EmptyStackException.
Required isEmpty check -
if(gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
AddBridges state = gameTree.peek();
As, no actions have been performed by function but while loop. In case other instcutions to be processed , a "break" is required.
Related
I am making a sudoku solver for a school assignment in Java, using backtracking. I have successfully solved it using a boolean solveSudoku() method but in the template provided by the teacher, the method is listed as void solveSudoku(). It clearly says in the requirements to use the methods as given, but I am having a hard time changing my code.
This is my initial method :
(The int[] findEmptySquare(), as the name suggests returns the indexes of the next empty cell in the sudoku.)
boolean solveSudoku() {
int[] arr = new int[2];
arr = findEmptySquare();
if (arr != null) {
for (int i = 1; i <= 9; i++) {
if (givesConflict(row, col, i) == false) {
int x = row, y = col;
grid[row][col] = i;
if (solveSudoku()) {
return true;
} else {
grid[row][col] = 0;
row = x;
col = y;
}
}
}
return false;
}
//solutionCounter++;
return true;
}
I have tried to mimic the boolean modus operandi in a void method like this :
boolean okay;
void solveSudoku() {
int[] arr = new int[2];
arr = findEmptySquare();
if (arr != null) {
for (int i = 1; i <= 9; i++) {
if (givesConflict(row, col, i) == false) {
int x = row, y = col;
grid[row][col] = i;
if (okay == true) { //line 11
solveSudoku();
okay = true;
return;
} else {
grid[row][col] = 0;
row = x;
col = y;
}
}
}
okay = false;
return;
}
okay = true;
return;
}
Unfortunately, this does not work as intended. If on line 11 I replace it with if(okay == false), the program runs until it gets stuck with no correct number to fill, and then the backtracking doesn't start.
Could anyone provide a hint in what way I can successfully tranform this method?
I would greatly appreciate it, thank you!
My approach seems to be correct , the issue is whether multiple trips are allowed.
My solution seems to exceed the correct answer.
The Question:
In a N x N grid representing a field of cherries, each cell is one of three possible integers.
0 means the cell is empty, so you can pass through;
1 means the cell contains a cherry, that you can pick up and pass through;
-1 means the cell contains a thorn that blocks your way.
Your task is to collect maximum number of cherries possible by following the rules below:
Starting at the position (0, 0) and reaching (N-1, N-1) by moving right or down through valid path cells (cells with value 0 or 1);
After reaching (N-1, N-1), returning to (0, 0) by moving left or up through valid path cells;
When passing through a path cell containing a cherry, you pick it up and the cell becomes an empty cell (0);
If there is no valid path between (0, 0) and (N-1, N-1), then no cherries can be collected.
My Solution:
class Solution {
public int cherryPickup(int[][] grid) {
int N = grid.length;
int[][] dp;
char[][] track;
boolean f = true;
int sum = 0;
int count = 0;
while(f)
{
dp = new int[N][N];
track = new char[N][N];
dp[0][0] = grid[0][0];
track[0][0] = 'a';
char c;
c='u';
for(int i=1;i<N;i++)
{
if(c=='r'||grid[i][0]==-1)
{
c='r';
dp[i][0]=-1;
}
else
dp[i][0] = dp[i-1][0] + grid[i][0];
track[i][0] = c;
}
c='s';
for(int j=1;j<N;j++)
{
if(c=='r'||grid[0][j]==-1)
{
c='r';
dp[0][j]=-1;
}
else
dp[0][j] = dp[0][j-1] + grid[0][j];
track[0][j] = c;
}
for(int i=1;i<N;i++)
for(int j=1;j<N;j++)
{
if(grid[i][j]==-1||(track[i-1][j]=='r' && track[i][j-1]=='r'))
{
track[i][j] = 'r';
dp[i][j] = -1;
}
else
{
if(dp[i-1][j]>=dp[i][j-1])
{
track[i][j] = 'u';
dp[i][j] = dp[i-1][j] + grid[i][j];
}
else
{
track[i][j] = 's';
dp[i][j] = dp[i][j-1] + grid[i][j];
}
}
}
if(dp[N-1][N-1]<=0)
break;
sum += dp[N-1][N-1];
int r = N-1 , cr = N-1;
while(r>0||cr>0)
{
grid[r][cr]=0;
if(track[r][cr]=='s')
cr--;
else
r--;
}
grid[0][0] = 0;
}
return sum;
}
}
Can someone help??
It exists a 2 Dimensional Array for a field of (x,y) length, here for instance 9x6. What I need to do here is to check how many free fields are around the Orange and Red Star. The black (filled) fields represent the occupied fields. In this example for instance I have 7 free fields for Orange, 1 for Red. I know that I can loop through each field and see whether one field is occupied or not, but how could I loop through so that I know that these non-occupied fields are next to the Star or in the Radius of the Star of non-occupied fields? I hope I could elaborate my question well.
Field[][] fields = new Field[9][6];
private void checkEmptyFields(Star star) {
for (int i = 0; i < 9; i++){ // Hardcoded size as an example
for (int j = 0; i < 6; i++) {
if(fields[i][j].isOccupied())
{
//It is occupied, but what now?
}
}
}
}
isOccupied Function:
public boolean isOccupied(){
return occupied;
}
I expect the output to be in this example Orange: 7, Red: 1 (because Red is blocked by the Orange Star and the occupied boxes)
This seems like a problem where breadth-first-search is the appropriate algorithm to use here. Breadth-first-search, or BFS, is when you visit all of a node's, or in this case fields', neighbors first. In your case, "visiting", will just mean checking if it's occupied or not. If it the neighboring field is not occupied and hasn't been visited before, then you can search that field and it's neighbors. The order in which you search is determined by using a Queue-like data structure like so,
private void checkEmptyFields(Star star) {
boolean visited[9][6] = new visited[9][6];
//get the star's coordinates somehow, you may have to change this
int i = star.row;
int j = star.col;
visited[i][j] = true;
int freeFieldCount = 0;
Queue<Field> q = new LinkedList<Field>();
q.add(fields[i][j]);
while(!q.isEmpty()) {
Field current = q.poll();
//get the coordinates from the field, you may have to change this
i = current.row;
j = current.col;
int rowUpperLimit = i + 1;
int rowLowerLimit = i - 1;
int colUpperLimit = j + 1;
int colLowerLimit = j - 1;
if(rowUpperLimit >= 9) {
rowUpperLimit = 8;
}
if(rowLowerLimit < 0) {
rowLowerLimit = 0;
}
if(colUpperLimit >= 6) {
colUpperLimit = 5;
}
if(colLowerLimit < 0) {
colUpperLimit = 0;
}
//check immediate neighbors
for(int m = rowLowerLimit; m <= rowUpperLimit; m++) {
for(int n = colLowerLimit; n <= colUpperLimit; n++) {
if((m != i && n != j) && !visited[m][n] && !fields[m][n].isOccupied()) {
freeFieldCount++;
visited[m][n] = true;
q.add(fields[m][n]);
}
}
}
}
return freeFieldCount;
}
As user #juvian mentioned, this is an 8-neighbor approach. If you want to do a 4-neighbor approach, simply visit only the neighbors immediately to the left, right, above, or below the current field. You can modify the while loop like so,
while(!q.isEmpty()) {
Field current = q.poll();
//get the coordinates from the field, you may have to change this
i = current.row;
j = current.col;
int rowUpperLimit = i + 1;
int rowLowerLimit = i - 1;
int colUpperLimit = j + 1;
int colLowerLimit = j - 1;
if(colLowerLimit > -1) {
//check neighbor to the left
if(!visited[i][colLowerLimit] && !fields[i][colLowerLimit].isOccupied()) {
freeFieldCount++;
visited[i][colLowerLimit] = true;
q.add(fields[i][colLowerLimit]);
}
}
if(colUpperLimit < 6) {
//check neighbor to the right
if(!visited[i][colUpperLimit] && !fields[i][colUpperLimit].isOccupied()) {
freeFieldCount++;
visited[i][colUpperLimit] = true;
q.add(fields[i][colUpperLimit]);
}
}
if(rowLowerLimit > -1) {
//check neighbor below
if(!visited[rowLowerLimit][j] && !fields[rowLowerLimit][j].isOccupied()) {
freeFieldCount++;
visited[rowLowerLimit][j] = true;
q.add(fields[rowLowerLimit][j]);
}
}
if(rowUpperLimit < 9) {
//check neighbor above
if(!visited[rowUpperLimit][j] && !fields[rowUpperLimit][j].isOccupied()) {
freeFieldCount++;
visited[rowUpperLimit][j] = true;
q.add(fields[rowUpperLimit][j]);
}
}
}
}
I have a android application called Islands and bridges also known as Hashiwokakero
The application uses A 2 Dimensional array that spawns the Islands randomly everytime the user restarts the game It form a Matrix with number from 0 to 4 where 0=null and 1-4 = Island There can be 2 bridges comming out of one Island to connect the other , The map at the moment is not solvable. To solve the game the user needs to connect the islands using bridges so if an island = 4 it needs 4 connection to it if an island = 2 it needs 2 connection and so on..
in my research i found out that the best algorithm to solve the game is to use Depth first search - article
I have looked at different question on here but can't seem to find a solution as my array is of type String rather than integer.
QUESTION how can apply a DFS algorithm to connect the islands?
here is a screenshot of my application.
This the function to create a easy map 4x4 matrix:
private void InitializeEasy() {
Random rand = new Random();
String[][] debug_board_state = new String[4][4];
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state.length; row++) {
for (int column = 0; column < debug_board_state[row].length; column++) {
debug_board_state[row][column] = String.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state.length; row++) {
for (int column = 0; column < debug_board_state[row].length; column++) {
System.out.print(debug_board_state[row][column] + " ");
}
System.out.println();
}
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
for (int colNum = column - 1; colNum <= (column + 1); colNum += 1) {
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.parseInt(debug_board_state[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
}
DFS could be applied to the game state.
Pseudo algorithm:
pick a random (or by some other criterium) island that still needs bridges
build a bridge between this island and one of its neighbors (obviously a neighbor that also needs a bridge)
push the new state of the game (for instance the connectivity matrix of this graph) on a stack
if the game contains inconsistencies, pop 1 item from the stack
go back to step 1, using the top of the stack as the current state
As I mentioned, this is a piece of pseudo-code.
You will need to refine it to handle edge-cases.
You should also think about strategies to prevent the branching factor from becoming too large.
example (not thoroughly tested, not thoroughly debugged):
int[][] STARTING_CLUES = {
{2, 0, 0, 3, 0, 3},
{0, 1, 4, 0, 4, 0},
{0, 0, 0, 0, 0, 0},
{3, 0, 3, 0, 2, 0},
{0, 0, 0, 1, 0, 2},
{2, 0, 4, 0, 2, 0}
};
void search(){
Map<Point, List<Direction>> remainingOptions = new HashMap<>();
Stack<Land> gameTree = new Stack<>();
gameTree.push(new Land(STARTING_CLUES));
while(true){
Land state = gameTree.peek();
int[] p = state.lowestTodo();
if (p == null)
System.out.println("solution found");
// move to next game state
int r = p[0];
int c = p[1];
System.out.println("expanding game state for node at (" + r + ", " + c + ")");
List<Direction> ds = null;
if(remainingOptions.containsKey(new Point(r,c)))
ds = remainingOptions.get(new Point(r,c));
else{
ds = new ArrayList<>();
for(Direction dir : Direction.values()) {
int[] tmp = state.nextIsland(r, c, dir);
if(tmp == null)
continue;
if(state.canBuildBridge(r,c,tmp[0], tmp[1]))
ds.add(dir);
}
remainingOptions.put(new Point(r,c), ds);
}
// if the node can no longer be expanded, and backtracking is not possible we quit
if(ds.isEmpty() && gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(r,c));
System.out.println("going back to previous decision");
continue;
}
Direction dir = ds.remove(0);
System.out.println("connecting " + dir.name());
remainingOptions.put(new Point(r,c), ds);
Land nextState = new Land(state);
int[] tmp = state.nextIsland(r,c,dir);
nextState.connect(r,c, tmp[0], tmp[1]);
gameTree.push(nextState);
}
}
public static void main(String[] args) {
new Main().search();
}
I also wrote a utility class that handles the common operations on the piece of land on which bridges need to be built (like finding the next available island, checking whether a bridge can be built, etc)
public class Land {
private int[][] BRIDGES_TO_BUILD;
private boolean[][] IS_ISLAND;
private Direction[][] BRIDGES_ALREADY_BUILT;
public Land(int[][] bridgesToDo){
BRIDGES_TO_BUILD = copy(bridgesToDo);
int R = bridgesToDo.length;
int C = bridgesToDo[0].length;
BRIDGES_ALREADY_BUILT = new Direction[R][C];
IS_ISLAND = new boolean[R][C];
for(int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
BRIDGES_ALREADY_BUILT[i][j] = null;
IS_ISLAND[i][j] = bridgesToDo[i][j] > 0;
}
}
}
public Land(Land other){
BRIDGES_TO_BUILD = copy(other.BRIDGES_TO_BUILD);
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
BRIDGES_ALREADY_BUILT = new Direction[R][C];
IS_ISLAND = new boolean[R][C];
for(int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
BRIDGES_ALREADY_BUILT[i][j] = other.BRIDGES_ALREADY_BUILT[i][j];
IS_ISLAND[i][j] = other.IS_ISLAND[i][j];
}
}
}
public int[] next(int r, int c, Direction dir){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
// out of bounds
if(r < 0 || r >=R || c < 0 || c >= C)
return null;
// motion vectors
int[][] motionVector = {{-1, 0},{0,1},{1,0},{0,-1}};
int i = Arrays.asList(Direction.values()).indexOf(dir);
// calculate next
int[] out = new int[]{r + motionVector[i][0], c + motionVector[i][1]};
r = out[0];
c = out[1];
// out of bounds
if(r < 0 || r >=R || c < 0 || c >= C)
return null;
// return
return out;
}
public int[] nextIsland(int r, int c, Direction dir){
int[] tmp = next(r,c,dir);
if(tmp == null)
return null;
while(!IS_ISLAND[tmp[0]][tmp[1]]){
tmp = next(tmp[0], tmp[1], dir);
if(tmp == null)
return null;
}
return tmp;
}
public boolean canBuildBridge(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
return canBuildBridge(r0, c1, r1, c0);
}
if(c0 == c1 && r0 > r1){
return canBuildBridge(r1, c0, r0, c1);
}
if(r0 == r1){
int[] tmp = nextIsland(r0, c0, Direction.EAST);
if(tmp[0] != r1 || tmp[1] != c1)
return false;
if(BRIDGES_TO_BUILD[r0][c0] == 0)
return false;
if(BRIDGES_TO_BUILD[r1][c1] == 0)
return false;
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
if(BRIDGES_ALREADY_BUILT[r0][i] == Direction.NORTH)
return false;
}
}
if(c0 == c1){
int[] tmp = nextIsland(r0, c0, Direction.SOUTH);
if(tmp[0] != r1 || tmp[1] != c1)
return false;
if(BRIDGES_TO_BUILD[r0][c0] == 0 || BRIDGES_TO_BUILD[r1][c1] == 0)
return false;
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
if(BRIDGES_ALREADY_BUILT[i][c0] == Direction.EAST)
return false;
}
}
// default
return true;
}
public int[] lowestTodo(){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
int[] out = {0, 0};
for (int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
if(BRIDGES_TO_BUILD[i][j] == 0)
continue;
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0)
out = new int[]{i, j};
if (BRIDGES_TO_BUILD[i][j] < BRIDGES_TO_BUILD[out[0]][out[1]])
out = new int[]{i, j};
}
}
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0) {
return null;
}
return out;
}
private int[][] copy(int[][] other){
int[][] out = new int[other.length][other.length == 0 ? 0 : other[0].length];
for(int r=0;r<other.length;r++)
out[r] = Arrays.copyOf(other[r], other[r].length);
return out;
}
public void connect(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
connect(r0, c1, r1, c0);
return;
}
if(c0 == c1 && r0 > r1){
connect(r1, c0, r0, c1);
return;
}
if(!canBuildBridge(r0, c0, r1, c1))
return;
BRIDGES_TO_BUILD[r0][c0]--;
BRIDGES_TO_BUILD[r1][c1]--;
if(r0 == r1){
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
BRIDGES_ALREADY_BUILT[r0][i] = Direction.EAST;
}
}
if(c0 == c1){
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
BRIDGES_ALREADY_BUILT[i][c0] = Direction.NORTH;
}
}
}
}
I'm trying to find the indices of all the local minima and maxima within an Array.
Example:
int[] array = {5,4,3,3,3,3,3,2,2,2, 6,6,8,5,5,5,3,3,2,1, 1,4,4,7};
// | | |
// Indices: 0,1,2,3,4,5,6,7,8,9, 10,1,2,3,4,5,6,7,8,9, 20,1,2,3
// Minima: 8, 20
// Maxima: 12
I came up with an algorithm about which I have few questions:
Is there a much better one? :)
I used an Enum with methods to achieve this dualism that UP and STRAIGHT_UP are both "UP". Seems messy to me. Any suggestions?
Do you have better method-names? direction() (+return value) kind of implies that STRAIGHT is not a dir. But at the same time it is, since its an element in the Emum. Hm.
It works for the given array. Do you see a situation where it does not?
-
import java.util.ArrayList;
public class MinMaxFinder {
private int[] array;
private ArrayList<Integer> minima;
private ArrayList<Integer> maxima;
private enum Direction{
UP, DOWN, STRAIGHT_UP, STRAIGHT_DOWN, STRAIGHT;
public Direction direction(){
if(this==UP || this==STRAIGHT_UP){
return UP;
}else if(this==DOWN || this==STRAIGHT_DOWN){
return DOWN;
}else{
return STRAIGHT;
}
}
public boolean isStraight(){
if(this==STRAIGHT_DOWN || this==STRAIGHT_UP || this==STRAIGHT){
return true;
}else{
return false;
}
}
public boolean hasDifferentDirection(Direction other){
if(this!=STRAIGHT && other!=STRAIGHT && this.direction() != other.direction() ){
return true;
}
return false;
}
}
public MinMaxFinder(int[] array){
this.array = array;
}
public void update() {
minima = new ArrayList<Integer>();
maxima = new ArrayList<Integer>();
Direction segmentDir = Direction.DOWN;
int indexOfDirectionChange = 0;
int prevVal = array[0];
int arrayLength = array.length;
for(int i=1; i<arrayLength; i++){
int currVal = array[i];
Direction currentDir = currVal<prevVal?Direction.DOWN:(currVal>prevVal?Direction.UP:Direction.STRAIGHT);
prevVal = currVal;
if(currentDir.hasDifferentDirection(segmentDir)){
int changePos = (indexOfDirectionChange+i-1)/2;
if(currentDir.direction() == Direction.DOWN){
maxima.add(changePos);
}else{
minima.add(changePos);
}
segmentDir = currentDir;
indexOfDirectionChange = i;
}else if( currentDir.isStraight() ^ segmentDir.isStraight() ){
indexOfDirectionChange = i;
if(currentDir.isStraight() && segmentDir.direction()==Direction.UP){
segmentDir=Direction.STRAIGHT_UP;
}else if(currentDir.isStraight() && segmentDir.direction()==Direction.DOWN){
segmentDir=Direction.STRAIGHT_DOWN;
}else{
segmentDir = currentDir;
}
}
}
}
public ArrayList<Integer> getMinima() {
return minima;
}
public ArrayList<Integer> getMaxima() {
return maxima;
}
}
Consider an array of first differences d[i] = a[i] - a[i-1].
If d[i] is positive, then a increased over the last step and if d[i] is negative then a decreased. So, a change in sign of d from positive to negative indicates a was increasing, now decreasing, a local max. Similarly, negative to positive indicates a local min.
Something like this "should" work and it's probably conceptually less complicated.
Scans the array once and registers mins and maxs.
Things worth mentioning:
1) The if(direction < 0){}else{} can probably be removed, but I didn't have time to think about the details.
2) The key idea is, depending on the first "direction" (whether we see a min or a max first), the for loops order changes.
3) in case of multiple items, it will always keep the last element (highest index).
if(a.length < 2){
return;
}
List<Integer> mins = new ArrayList<Integer>();
List<Integer> maxs = new ArrayList<Integer>();
int i=1;
int prev = 0;
int direction = 0;
for(int j=1, k = 0;j<a.length && (direction = a[j]-a[k]) == 0;j++, k++);
if(direction == 0){
//Array contains only same value.
return;
}
if(direction < 0){
while(i<a.length){
for(;i<a.length && a[prev] >= a[i];i++,prev++);
mins.add(prev);
for(;i<a.length && a[prev] <= a[i];i++,prev++);
maxs.add(prev);
i++;prev++;
}
}
else{
while(i<a.length){
for(;i<a.length && a[prev] <= a[i];i++,prev++);
maxs.add(prev);
for(;i<a.length && a[prev] >= a[i];i++,prev++);
mins.add(prev);
i++;prev++;
}
}
//maxs and mins now contain what requested
I think i got it. Thanks guys! Your ideas helped me a lot!
The following solution will do for me:
ArrayList<Integer> mins = new ArrayList<Integer>();
ArrayList<Integer> maxs = new ArrayList<Integer>();
int prevDiff = array[0] - array[1];
int i=1;
while(i<array.length-1){
int currDiff = 0;
int zeroCount = 0;
while(currDiff == 0 && i<array.length-1){
zeroCount++;
i++;
currDiff = array[i-1] - array[i];
}
int signCurrDiff = Integer.signum(currDiff);
int signPrevDiff = Integer.signum(prevDiff);
if( signPrevDiff != signCurrDiff && signCurrDiff != 0){ //signSubDiff==0, the case when prev while ended bcoz of last elem
int index = i-1-(zeroCount)/2;
if(signPrevDiff == 1){
mins.add( index );
}else{
maxs.add( index );
}
}
prevDiff = currDiff;
}