How to use memoization in counting a large number of matrices - java

I have been given a program, which requires me to count the number of previous states for a matrix.
The given matrix is a boolean matrix. I will use 1 for true and 0 for false to explain the program.
The next state of a cell in a matrix is 1 if, considering these four cells:
the cell itself
the cell right to it
the cell below it
the cell below it, and to its right,
there is only one 1 in all these 4 cells, i.e., there are exactly 3 0s and exactly 1 1 in these 4 cells.
If the given matrix (M) is :
1 1 0 0
0 0 0 1
0 0 1 0
Then for the first cell (M[0][0]), the four cells to be considered are M[0][0], M[0][1], M[1][0] and M[1][1]. So, the next state of the first cell is 0, because we have 2 1 in these 4 cells.
For the second cell (M[0][1]), the four cells to be considered are M[0][1], M[0][2], M[1][1], M[1][2]. So the next state for this cell is 1 because there is only 1 1 in these four cells.
Going this way, the next state for this matrix(M) would be the matrix (N):
0 1 1
0 1 0
The next state will, obviously, be 1 row and 1 column less than the previous state. Thus, a given state of the matrix can have many previous states, for example, besides matrix M, the given matrix :
1 0 1 0
1 0 0 0
1 1 0 0
will also have the next state N.
I have to count the number of previous states that a given matrix has.
I have written the following code :
public class Answer2 {
static boolean[][] main_array,answer_array; // answer_array is the 2D array given to me. main_array is the 2D array which I recurse through, and then find its solution to compare with answer_array.
static int c; // counter
static int answer_array_height,answer_array_width; // matrix height and matrix width
public static int answer(boolean[][] boolean_array)
{
answer_array = boolean_array;
main_array = new boolean[answer_array.length+1][answer_array[0].length+1];
c=0;
answer_array_height = answer_array.length;
answer_array_width = answer_array[0].length;
recurse(1,1);
main_array[0][0] = true;
recurse(1,1);
return c;
}
public static String pad(String s, int l){ //Add 0's to the beginning of the string till its length equals l
for(int i=s.length(); i<l; i++)
s='0'+s;
return s;
}
public static void recurse(int w, int h){
if(w==answer_array_width+1 && h==answer_array_height+1){
c++;
return;
}
//System.out.println(java.util.Arrays.deepToString(main_array).replace("],","],\n").replace("true","1").replace("false","0"));
if(h==answer_array_height+1 || h>=w){//Add column
int x = 0;
for(int i=0; i<h; i++) x+=(int)Math.pow(2,i); //This will give me the integer representation of max value(whose binary representation will be used to put values in the matrix) to handle.
for(int i=0; i<=x; i++){
String str = pad(Integer.toBinaryString(i),h);
for(int j=0; j<h; j++){
main_array[j][w]= str.charAt(j)=='1'; //set main_array[j][w] true wherever the binary representation of i has 1. This recurses through all the combinations.
}
if(check(w+1,h,false)){
recurse(w+1, h);
}else{
for(int j=0; j<h; j++){
main_array[j][w]=false;
}
}
}
}else{//Add row
int x = 0;
for(int i=0; i<w; i++) x+=(int)Math.pow(2,i);
for(int i=0; i<=x; i++){
String str = pad(Integer.toBinaryString(i),w);
for(int j=0; j<w; j++){
main_array[h][j]= str.charAt(j)=='1';
}
if(check(w,h+1,true)){
recurse(w, h+1);
}else{
for(int j=0; j<w; j++){
main_array[h][j]=false;
}
}
}
}
}
// w is the effective width, h is the effective height, height_was_increased is true if height was increased, false if width was increased.
//height_was_increased helps to shorten the time used for comparison as the matrix was correct before the width or height was increased. So it just needs to check the increased portion.
public static boolean check(int w, int h, boolean height_was_increased){
if(height_was_increased){
for(int j=0; j<w-1; j++){
//I know this part is complex. It just finds out the answer of the four cells to be considered and matches it with the given matrix.
if(answer_array[h-2][j] != (main_array[h-2][j]^main_array[h-2+1][j]^main_array[h-2][j+1]^main_array[h-2+1][j+1] && !(main_array[h-2][j] && main_array[h-2+1][j]) && !(main_array[h-2][j+1] && main_array[h-2+1][j+1]))) return false;
}
}else{
for(int i=0; i<h-1; i++){
if(answer_array[i][w-2] != (main_array[i][w-2]^main_array[i+1][w-2]^main_array[i][w-2+1]^main_array[i+1][w-2+1] && !(main_array[i] [w-2] && main_array[i+1][w-2]) && !(main_array[i][w-2+1] && main_array[i+1][w-2+1]))) return false;
}
}
return true;
}
}
What it basically does, is that it begins with an empty matrix (of the appropriate size for its next state that gives the matrix asked for) and starts from the top left corner, increasing the effective width and height alternately by 1, and checking if the next state of the matrix till now corresponds to the given state. If not, it skips the rest of the matrix. Then, if a matrix whose next state is the same as the given state is found, it increases the counter by 1.
This code works for small matrices (no. of cells <40), but it takes a lot of time for large matrices. The maximum width of the matrix can be 50 and the maximum height can be 9. So this code doesn't quite work for that purpose.
I know that I have to use memoization here (doing c++ thousands of times is just not right!) But I can't imagine how to implement it. I have previously written programs using dynamic programming, but have no idea where it would be used here. Any help would be appreciated.

There are lot of possible matrices that produce given next state. If next state matrix N is given and initial matrix M is partially filled, for example elements m[x][y+1], m[x+1][y], and m[x+1][y+1]
are filled, than possibilities for element m[x][y] are checked with value s = m[x][y+1] + m[x+1][y] + m[x+1][y+1], in a way:
if n[x][y] == 1:
if s == 0 than m[x][y] = 1
if s == 1 than m[x][y] = 0
if s > 1 than m[x][y] can't be filled
if n[x][y] == 0:
if s == 0 than m[x][y] = 0
if s == 1 than m[x][y] = 1
if s > 1 than m[x][y] = 0 or 1
It looks like values 1 in N 'filter' combinations and values 0 in N 'multiply' them.
Since height is bounded by smaller value I suggest approach first to fill last column with possible
values, than pass columns backward, fill last column element and than by upper check fill element by element.
Python implementation:
import numpy
from itertools import product
num_results = 0
def fill_xy(m, s, x, y):
if y < 0:
fill_x_last(m, s, x-1)
return
_sum = s[x+1, y] + s[x+1, y+1] + s[x, y+1]
if m[x, y] == 1:
if _sum == 0:
s[x, y] = 1
elif _sum == 1:
s[x, y] = 0
else:
return
else:
if _sum == 0:
s[x, y] = 0
elif _sum == 1:
s[x, y] = 1
else:
s[x, y] = 0
fill_xy(m, s, x, y-1)
s[x, y] = 1
fill_xy(m, s, x, y-1)
def fill_x_last(m, s, x):
global num_results
if x < 0:
print s
num_results += 1
else:
s[x, s.shape[1]-1] = 0
fill_xy(m, s, x, s.shape[1]-2)
s[x, s.shape[1]-1] = 1
fill_xy(m, s, x, s.shape[1]-2)
def solve(m):
global num_results
height = m.shape[1]+1
s = numpy.zeros((m.shape[0]+1, height), dtype=numpy.uint8)
for p in product((0, 1), repeat=height):
s[-1, :] = p
fill_x_last(m, s, s.shape[0]-2)
print num_results
solve(numpy.array([[0, 1, 1], [0, 1, 0]], dtype=numpy.uint8))

Related

Lights Out - finding worst initial state

I have a task revolving around a small game, called Lights Out.
Game
The game consists of a board with dimensions 3x3, where each cell can either be 1 or 0, for example:
0 1 0
1 1 0
0 0 0
the game is said to be solved when all cells are 1, so:
1 1 1
1 1 1
1 1 1
and in each turn the user can click any cell which will flip its state and the state of the neighbors to the left, right, above and below (if they exist). So clicking on the cell in the middle of the first example board will yield:
0 0 0
0 0 1
0 1 0
Task
Now I have to find the worst possible initial board for the game and also figure out how many turns it needs to the solved state if played optimal.
Attempt
I tried to write a recursive solver which, given an initial board, finds the optimal sequence of turns to solve the game. And after that I wanted to feed it with all possible initial boards.
However, the recursion runs into a stack overflow. So I probably have to rewrite it in an iterative fashion. How can I do that?
Here is the code, as minimal complete example:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
public class GameTest {
public static void main(String[] args) {
boolean[][] board = {
{false, false, false},
{false, true, false},
{false, false, false}
};
List<GameState> solutionPath = GameSolver.solve(board);
printSolutionPath(solutionPath);
}
private static void printSolutionPath(List<GameState> solutionPath) {
System.out.printf("Solution path uses %d turns%n", solutionPath.get(solutionPath.size() - 1).getTurns());
String turnProgression = solutionPath.stream()
.map(state -> String.format("[%d|%d]", state.getX(), state.getY()))
.collect(Collectors.joining(" -> "));
System.out.println("Turns are: " + turnProgression);
System.out.println("Board progression is:");
for (GameState state : solutionPath) {
System.out.println(state.boardToString());
System.out.println("-----");
}
}
private static class GameSolver {
public static List<GameState> solve(boolean[][] initialBoard) {
GameState state = new GameState(initialBoard);
return solve(state);
}
public static List<GameState> solve(GameState state) {
// Base case
if (state.isSolved()) {
return List.of(state);
}
// Explore all other solutions
List<List<GameState>> solutionPaths = new ArrayList<>();
boolean[][] board = state.getBoard();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[x].length; y++) {
solutionPaths.add(solve(new GameState(state, x, y)));
}
}
List<GameState> bestSolutionPath = Collections.min(solutionPaths, Comparator.comparingInt(solutionPath -> solutionPath.get(solutionPath.size() - 1).getTurns()));
bestSolutionPath.add(state);
return bestSolutionPath;
}
}
private static class GameState {
private boolean[][] board;
private int turns;
private int x;
private int y;
public GameState(boolean[][] board) {
this.board = board;
turns = 0;
x = -1;
y = -1;
}
public GameState(GameState before, int x, int y) {
board = before.board;
click(x, y);
turns++;
this.x = x;
this.y = y;
}
public boolean isSolved() {
for (boolean[] row : board) {
for (boolean state : row) {
if (!state) {
return false;
}
}
}
return true;
}
public int getTurns() {
return turns;
}
public boolean[][] getBoard() {
return board;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String boardToString() {
StringBuilder sb = new StringBuilder();
for (int x = 0; x < board.length; x++) {
StringJoiner row = new StringJoiner(" ");
for (int y = 0; y < board[x].length; y++) {
row.add(board[x][y] ? "1" : "0");
}
sb.append(row);
}
return sb.toString();
}
private void click(int centerX, int centerY) {
toggle(centerX, centerY);
toggle(centerX, centerY - 1);
toggle(centerX, centerY + 1);
toggle(centerX - 1, centerY);
toggle(centerX + 1, centerY);
}
private void toggle(int x, int y) {
if (x < 0 || y < 0 || x >= board.length || y >= board[x].length) {
return;
}
board[x][y] = !board[x][y];
}
}
}
Algorithm
If possible, I would also be interested in pure-mathematical arguments that solve or prove this without writing code that solves it by trying out.
The "Lights Out" problem can be simplified by observing that the moves are commutative, i.e. if you flip the plus-shapes centred on a certain set of cells, then it doesn't matter which order you flip them in. So an actual ordered path through a graph is not needed. We can also observe that each move is self-inverse, so no solution requires making the same move more than once, and if a set of moves m is a solution to a position p, then m also produces the position p starting from an empty board.
Here's a short solution in Python based on this observation: I've solved it for the goal of all 0s, i.e. the "lights" are "out", but it is trivial to change it to solve for the goal of all 1s.
The constant list masks represents which cells should be flipped for each of the 9 possible moves.
The bitcount function is used to measure how many moves a solution takes, given a bitmask representing a subset of the 9 possible moves.
The position function computes the board position after a set of moves is made, using the exclusive-or operation to accumulate the results of multiple flips.
The positions dictionary maps each reachable board position to a list of move-sets which produce it starting from an empty board. It turns out that all positions are reachable by exactly one set of moves, but if this is not known in advance then a dictionary of lists gives a more general solution.
The max(..., min(...)) part finds the position maximising the minimum number of moves needed to solve it, as required.
masks = [
int('110100000', 2), int('111010000', 2), int('011001000', 2),
int('100110100', 2), int('010111010', 2), int('001011001', 2),
int('000100110', 2), int('000010111', 2), int('000001011', 2),
]
def bitcount(m):
c = 0
while m:
c += (m & 1)
m >>= 1
return c
def position(m):
r = 0
for i in range(9):
if (1 << i) & m:
r ^= masks[i]
return r
from collections import defaultdict
positions = defaultdict(list)
for m in range(2**9):
p = position(m)
positions[p].append(m)
solution = max(positions, key=lambda p: min(map(bitcount, positions[p])))
print('board:', bin(solution))
print('moves:', ', '.join(map(bin, positions[solution])))
Output:
board: 0b101010101
moves: 0b111111111
That is, the "worst initial position" is an X shape (all four corners plus the centre cell are 1s), and the solution is to perform all 9 moves.
I am proposing an iterative solution to solve this (and related problems) based on graph theory.
Shortest-Path-Problem (SSP)
The problem can be reformulated as shortest-path-problem and, by that, be solved with any standard SPP algorithm, for example Dijkstr's algorithm.
For that, we will interpret all possible game boards as vertices and the action of clicking cells as edges of a graph.
For example
0 1 0
1 1 0
0 0 0
will be a vertex in the graph with 9 outgoing edges in total (one for each cell to click at). So we will for example have an edge
0 1 0 0 0 0
1 1 0 --> 0 0 1
0 0 0 0 1 0
with cost 1. All edge costs will be 1, indicating counting turns.
Given an initial board, like above, we formulate the SPP as the task of finding the shortest path in this graph from the vertex representing the initial board to the vertex representing the solved state
1 1 1
1 1 1
1 1 1
By using standard algorithms for solving SSP we receive the optimal path and its total cost. The path is the sequence of game states and the total cost is the amount of turns needed for that.
*-1 SPP
However, you are not only interested in solving given initial boards but also in finding the worst initial board and its optimal amount of turns.
This can be reformulated as a variant of the SPP family, namely trying to find the longest shortest path to the solved state. This is, among all shortest paths in the graph that end in the solved state, the path that maximizes the total cost.
This can be computed efficiently by a *-1 (many-to-one) SPP. That is, computing all shortest paths from any vertex to a single destination, which will be the solved state. And from those picking the path which has the greatest total cost.
Dijkstra's algorithm can compute that easily by executing the algorithm fully on a reversed graph (all edges reverse their direction) with the solved state as source, until it settled the whole graph (removing its stopping criteria).
Note that in your particular case graph reversal is not needed, as the graph in your game is bidirectional (any turn can be undone by executing it again).
Solution
Applying the above theory yields a pseudo-code looking like
Graph graph = generateGraph(); // all possible game states and turns
int[][] solvedState = [[1, 1, 1], [1, 1, 1], [1, 1, 1]];
List<Path> allShortestPaths = Dijkstra.shortestPathFromSourceToAllNodes(solvedState);
Path longestShortestPath = Collections.max(allPaths);
Some time ago I created a Java library for solving shortest path problems, Maglev. Using that library, the full code is:
import de.zabuza.maglev.external.algorithms.Path;
import de.zabuza.maglev.external.algorithms.ShortestPathComputationBuilder;
import de.zabuza.maglev.external.graph.Graph;
import de.zabuza.maglev.external.graph.simple.SimpleEdge;
import de.zabuza.maglev.external.graph.simple.SimpleGraph;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.StringJoiner;
public class GameTest {
public static void main(String[] args) {
Graph<GameState, SimpleEdge<GameState>> graph = generateGraph();
var algo = new ShortestPathComputationBuilder<>(graph).resetOrdinaryDijkstra()
.build();
GameState solvedState =
new GameState(new boolean[][] { { true, true, true }, { true, true, true }, { true, true, true } });
var pathTree = algo.shortestPathReachable(solvedState);
var longestShortestPath = pathTree.getLeaves()
.stream()
.map(pathTree::getPathTo)
.map(Optional::orElseThrow)
.max(Comparator.comparing(Path::getTotalCost))
.orElseThrow();
System.out.println("The longest shortest path has cost: " + longestShortestPath.getTotalCost());
System.out.println("The states are:");
System.out.println(longestShortestPath.iterator().next().getEdge().getSource());
for (var edgeCost : longestShortestPath) {
System.out.println("------------");
System.out.println(edgeCost.getEdge().getDestination());
}
}
private static Graph<GameState, SimpleEdge<GameState>> generateGraph() {
SimpleGraph<GameState, SimpleEdge<GameState>> graph = new SimpleGraph<>();
generateNodes(graph);
generateEdges(graph);
return graph;
}
private static void generateNodes(Graph<GameState, SimpleEdge<GameState>> graph) {
for (int i = 0; i < 1 << 9; i++) {
String boardString = String.format("%09d", Integer.parseInt(Integer.toBinaryString(i)));
graph.addNode(GameState.of(boardString, 3, 3));
}
}
private static void generateEdges(Graph<GameState, SimpleEdge<GameState>> graph) {
for (GameState source : graph.getNodes()) {
// Click on each field
boolean[][] board = source.getBoard();
for (int x = 0; x < board.length; x++) {
for (int y = 0; y < board[x].length; y++) {
GameState destination = new GameState(board);
destination.click(x, y);
graph.addEdge(new SimpleEdge<>(source, destination, 1));
}
}
}
}
private static class GameState {
public static GameState of(String boardString, int rows, int columns) {
boolean[][] board = new boolean[rows][columns];
int i = 0;
for (int x = 0; x < rows; x++) {
for (int y = 0; y < columns; y++) {
board[x][y] = boardString.charAt(i) == '1';
i++;
}
}
return new GameState(board);
}
private final boolean[][] board;
private GameState(boolean[][] board) {
this.board = new boolean[board.length][];
for (int x = 0; x < board.length; x++) {
this.board[x] = new boolean[board[x].length];
for (int y = 0; y < board[x].length; y++) {
this.board[x][y] = board[x][y];
}
}
}
public boolean[][] getBoard() {
return board;
}
#Override
public String toString() {
StringJoiner rowJoiner = new StringJoiner("\n");
for (int x = 0; x < board.length; x++) {
StringJoiner row = new StringJoiner(" ");
for (int y = 0; y < board[x].length; y++) {
row.add(board[x][y] ? "1" : "0");
}
rowJoiner.add(row.toString());
}
return rowJoiner.toString();
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final GameState gameState = (GameState) o;
return Arrays.deepEquals(board, gameState.board);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(board);
}
private void click(int x, int y) {
toggle(x, y);
toggle(x, y - 1);
toggle(x, y + 1);
toggle(x - 1, y);
toggle(x + 1, y);
}
private void toggle(int x, int y) {
if (x < 0 || y < 0 || x >= board.length || y >= board[x].length) {
return;
}
board[x][y] = !board[x][y];
}
}
}
Which yields the following solution to your problem:
The longest shortest path has cost: 9.0
The states are:
1 1 1
1 1 1
1 1 1
------------
1 0 1
0 0 0
1 0 1
------------
1 0 1
1 0 0
0 1 1
------------
1 1 0
1 0 1
0 1 1
------------
1 1 0
1 0 0
0 0 0
------------
1 1 0
1 1 0
1 1 1
------------
0 0 1
1 0 0
1 1 1
------------
1 0 1
0 1 0
0 1 1
------------
0 1 1
1 1 0
0 1 1
------------
0 1 0
1 0 1
0 1 0
So the worst initial game state is
0 1 0
1 0 1
0 1 0
and, if played optimally, it needs 9 turns to solve the game.
Some trivia, the game has 512 states in total (2^9) and 4608 possible moves.
Treat the puzzle as a graph per Zabuzard's answer, then perform breadth-first search starting from the solved node. The last node you reach is among the set having the longest-shortest path to the solution.
If possible, I would also be interested in pure-mathematical arguments that solve or prove this without writing code that solves it by trying out.
I am proposing a solution purely based on linear algebra.
Board as matrix
The game can be interpreted as set of linear equations which can be solved using standard linear equation solving techniques.
For that, a game board is interpreted as matrix .
In total, there are 9 possible actions (one for clicking each cell of the board). We encode which cells have to be flipped per action in 9 corresponding matrices:
where is the action-matrix corresponding to a click on cell in row i and column j.
Actions are commutative and self-inverse
Since entries are in , applying an action to a given board is as simple as adding the corresponding action-matrix to the board-matrix. For example:
This means that applying a set of actions is nothing else than some matrix additions. Matrix additions are commutative. This means that the order in which they are applied does not matter:
Moreover, any action-matrix is self-inverse on addition. Applying it again undoes the action, i.e.
This follows that, for any initial game board, we only have to apply each action at most once and the order in which we do that does not matter.
Linear equation system
This leads to the equation:
With the initial game board matrix L, coefficients which are 1 if the action should be applied, otherwise 0; and 1 being the all-ones matrix indicating that the game is won.
The equation can be simplified by moving L to the other side:
where L* is L but with all cells flipped.
Finally, this equation can be rewritten as standard linear system of equations Ax = b which can then be solved easily:
Since this matrix has maximal rank and a non-zero determinant , the game on a 3x3 board is always solvable and a solution is given by simply solving the linear equation system or by applying Cramer's rule.
Worst initial board
It also follows that the worst initial board is a matrix L that maximizes the coefficients being used, ideally all 9.
Turns out
0 1 0
1 0 1
0 1 0
is such an initial board which needs all 9 coeeficients to be set for a solution. I.e. solving the system
yields exactly one solution, namely for all i, j.
This can also be obtained from the opposite direction by setting all coefficients to 1 and solving for L instead:
which yields
0 1 0
1 0 1
0 1 0
for L again.

Algorithm not showing right output

The Question was:
You are given a binary matrix (i.e. each element of matrix is either 0 or 1) of size n × n. You want to re-arrange 1's in such a way that they form a rectangular region. Note that the rectangular region should be made of only 1's, and all the 1's of the entire matrix should be in this rectangular region.
For achieving rectangular region, you can swap any two elements of the matrix. Please find out the minimum number of swaps needed. If it is not possible to re-arrange 1's in the desired way, please print -1.
Input
First line of the input contains a single integer T denoting number of test cases.
Description of T test cases follows.
First line of each test case will contain a single integer n denoting dimension of matrix.
Each of next n lines will contain n space separated integers denoting the ith row of the matrix.
Output
For each test case, print a single line containing a single integer denoting minimum number of swaps needed or -1 depending on the situation.
Example
Input:
2
2
0 1
1 0
2
1 1
1 0
Output:
1
-1
Explanation
Example case 1. You can swap 1 of second row first column with 0 of first row first column.
After the swap, matrix will look as follows.
1 1
0 0
Here all the 1's form a rectangular region of dimension 1 × 2. In this case, 1 swap will be needed.
Note that you can also swap 1 at first row second column with 0 at second row second column too.
Matrix after this swap will be following.
0 0
1 1
So you need 1 swap in this case too.
So overall, you need 1 swap.
Example case 2. There is no way to create a rectangular region containing 3 1's in a matrix of dimension 2 × 2, hence answer is -1.
My Algorithm [Edit]
First i am Taking Number of Cases from user
Then the order of matrix [will be of nxn order].
So logic is that if matrix is 1x1 then it will simply print 0
else while taking input from user [that will be only 1 or 0] i am counting 1's because the logic i develop that when in a matrix of odd order the 1's will be even then it cannot be arranged in rectangular form.and for even order of matrix if 1's are odd , not arrange able .
Next i am traversing each index if i find one then i move to next element else i try to find 1 in the same colomn if dont find than i am breaking loop showing -1 that it is not arrange able in rectangular form
Than after arranging a row i check the next row whether it is already arranged or not if it is than i break everything and moves to next case
n rectangular form
My Solution
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
static long startTime;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int numberOfOnes = 0;
int T = scanner.nextInt();
for (int t = 1; t <= T; t++) {
int n = scanner.nextInt();
int loopCounter, swapCounter = 0;
boolean rowContainsZero = false;
int array[][] = new int[n][n];
boolean reject = true;
//Worst and the most simpler conditions
if (n == 1) {
System.out.print("0");
exitingSystem();
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
array[i][j] = scanner.nextInt();
if (array[i][j] == 1) {
numberOfOnes++;
}
}
}
if (n % 2 == 0 && numberOfOnes % 2 != 0) {
System.out.println("-1");
if (t == T) {
exitingSystem();
}
continue;
} else if (n % 2 != 0 && numberOfOnes % 2 == 0) {
System.out.println("-1");
if (t == T) {
exitingSystem();
}
continue;
}
// System.out.println("Here i am");
//From here swaping processes will take the place
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (array[i][j] == 1) {
continue;
} else if (array[i][j] == 0) {
loopCounter = i;
reject = true;
while (loopCounter < n) {
if (array[loopCounter][j] == 1) {
int temp = array[loopCounter][j];
array[loopCounter][j] = array[i][j];
array[i][j] = temp;
reject = false;
swapCounter += 1;
break;
}
loopCounter++;
}
if (rowContainsZero) {
System.out.println("" + swapCounter);
break;
}
if (reject == true) {
System.out.println("-1");
break;
} else {
for (int m = i + 1; m < n; m++) {
for (int k = 0; k < n; k++) {
if (array[m][k] == 0) {
rowContainsZero = true;
} else {
rowContainsZero = false;
break;
}
}
}
}
} else {
System.out.println("0's and 1's were Expected :(");
exitingSystem();
}
}
if (reject == true) {
break;
}
}
}
}
public static void exitingSystem() {
System.exit(0);
}
}
BUT THE CODECHEF COMPUTER SAYING WRONG ANSWER + They allowed to take input from keyboard too
I think your algorithm isn't fully correct.
I think the following is a counter-example for your step 4 / odd order (n=3) and even number of ones (numberOfOnes=4):
1 1 0
1 1 0
0 0 0
This should give 0.
Similar for n=4 and numberOfOnes=3:
1 1 1 0
0 0 0 0
0 0 0 0
0 0 0 0
This should give 0 as well.
I haven't yet deeply analyzed your steps 5 and 6.
Here are some more examples:
1 1 1 0
1 1 0 0
1 1 1 0
1 1 0 0
This should give -1, as from 10 ones you can only form rectangles of the form 2*5 or 1*10, which both don't fit into the 4*4 frame.
1 1 1 0
1 1 0 0
1 1 1 0
1 0 0 0
This should give 1, as by moving the lower-left 1 two palces up and right, you get a 3*3 rectangle.
This is not the way you are trying to solve problem. Suppose you have
0 0 1
0 1 1
0 0 1
This is a perfect example of solveable matrix but you can't simply use random swap and then acquire result. You need to use A* search algorithm with manhatten distance.
Make a priority queue
Define manhatten distance.
Create a function which creates succesors of each board. Like if i have above board then it will give you a collection of boards back:
0 0 1
0 1 1 ==> colection
0 0 1
0 1 1
0 0 1
0 0 1
0 0 1
0 0 1
0 1 1
0 1 0
0 1 1
0 0 1
0 0 1
0 1 1
0 1 0
Description of A:*
an initial lis to store visited boar so that you don't visit them again.
i will call MinPriority queue a pq
`insert the initial_board in pq
while(!pq.isEmpty() && !foundGoal(pq.min)) //You find goal when your
manhatten distance is 0.
board = pq.delMin(); //you have to override the distance method in
priority queue so it will return you that board whoms manhatten
distance is smallest.
for(boards b :board.getSuccesors(); // give you collection of boards.
if(notvisited(b,vistiedList)) // so that you don't come in same state again and
again.
pq.insert(b);
visitedList.add(b);`
In first year i had to solve 8-puzzle and you can solve this way however you can also use hamming distance but that's not efficient and here is 8-puzzle code(with A* implementation).

Triangle Puzzle: Find maximum total from top to bottom, starting at top moving moving to adjacent numbers

As the title suggests, I need to solve this puzzle.
5
9 6
4 6 8
0 7 1 5
The path I need to find is the max sum from top to bottom, only moving to adjacent children. So this path would be 5-9-6-7, with a sum of 27.
My code works for every set of data I input myself, but when I attempt the puzzles with the provided textFile's data, my sum/answer is not accepted as correct.
I cannot for the life of me figure out what is wrong with my code. Is there some exception I am not seeing?
public class Triangle
{
public static void main(String[] args) throws IOException
{
File file = new File("Tri.txt");
byte[] bytes = new byte[(int) file.length()];
try{
//Read the file and add all integers into an array with the correct size. Array size is found with number of bytes file.length()
//Parse string to integer
FileInputStream fis = new FileInputStream(file);
fis.read(bytes);
fis.close();
String[] valueStr = new String(bytes).trim().split("\\s+");
int[] list = new int[valueStr.length];
for (int i = 0; i < valueStr.length; i++)
list[i] = Integer.parseInt(valueStr[i]);
System.out.println(computeMaxPath(list));
}
catch(Exception e)
{
e.printStackTrace();
}
}
static int computeMaxPath(int[] list){
//Disregard row number one since it is the root. Start row number count at 2
int rowNumber = 2;
//set the sum to the value of the root.
int sum = list[0];
//selected index begins at the root, index 0
int selectedIndex = 0;
for (int j = 1; j < list.length; j=j+rowNumber)
{
// for every iteration the right child is found by adding the current selected index by z. What is z?
// the left child is of course found in the index -1 of the right child.
// z is the amount of of elements in the triangle's row. Row 3 has 3 elements, 4 has 4, etc.
// For exmaple, if the selectedIndex is index 4, its right child can be found by adding the index to the next row element count.
// 4 + 4 = 8 the right child is in index 8 and left is in index 7
int rightChildIndex = selectedIndex + rowNumber;
int leftChildIndex = selectedIndex + rowNumber - 1;
//set the appropriate index for the greater child's index
selectedIndex = list[rightChildIndex] >= list[leftChildIndex] ? rightChildIndex : leftChildIndex;
//increment the sum of the path
sum = sum + list[selectedIndex];
System.out.println(selectedIndex);
//increment the row number
rowNumber++;
}
return sum;
}
}
Essentially, my algorithm works by adding the string of ints from the text file into an array. The first selected index is of course the root node. To find the right child I add the selected index by the next row's length and subtract by 1 to find the left child index.
Any ideas?
This algorithm uses the wrong logic. In this case your algorithm works because it has the required properties to make your algorithm work, for other inputs this obviously not the case. For example consider the following (extreme) example:
1
1 0
0 0 9
Your algorithm works by simply always selecting the child with the larger sum, so in this case your algorithm would result in the path {1 , 1 , 0}, while the correct algorithm would result in {1 , 0 , 9}.
The correct algorithm would require to traverse the tree and search all paths in order to find the correct solution:
int findSum(int[] tree , int at_node){
if(at_node >= length(tree))
return 0 //end of the tree, quit recursive search
//maximum-path including node is the path with the greatest sum that includes either the left or right child of the node.
return max(findSum(tree , leftChild(at_node)) ,
findSum(tree , rightChild(at_node)) + tree[at_node]
}
As #JohnBollinger mentioned:
This top-to-bottom-approach is pretty simple. But on cost of efficiency. A more efficient, but also more efficient solution that only traverses each node exactly once. In the above stated algorithm a tree that represents the time each node was visited would look like a pascal's triangle, thus making 2 ^ height array-lookups. The bottom-top approach would only require height + height - 1 + ... + 1 lookups.
int findSumBottomTop(int[] tree , int height){
//initialize counter for previous level
int[] sums = new int[height + 1]
fill(sums , 0)
//counter for the level counts down to 1 (note that this variable is not 0-based!!!)
int lvl = height
//counter for nodes remaining on the current level (0-based)
int remaining_in_lvl = lvl - 1
//maximum-paths for each node on the current level
int[] next_level = new int[lvl]
//iterate over all nodes of the tree
for(int node = length(tree) - 1; node > -1 ; node--){
int left_max_path = sums[remaining_in_lvl]
int right_max_path = sums[remaining_in_lvl + 1]
next_level[remaining_in_lvl] = max(right_max_path , left_max_path) + tree[node]
//decrement counter for remaining nodes
remaining_in_lvl -= 1
if(remaining_in_lvl == -1){
//end of a level was encountered --> continue with lvl = lvl - 1
lvl--
//update to match length of next
remaining_in_lvl = lvl - 1
//setup maximum-path counters for next level
sums = next_level
next_level = new int[sums.length - 1]
}
//there is exactly one sum remaining, which is the sum of the maximum-path
return sums[0];
}
The basic idea of this would be the following:
Consider this example tree:
0 ^ 6
0 1 | 3 6
0 1 2 | 1 3 5
0 1 2 3 | 0 1 2 3
0 0 0 0 0
tree traversal sums
sums would be the values of sums that would be produced for each level. We simply start searching at the bottom and searching the maximum-path from each node in a level to the bottom. This would be the maximum of the maximum-path of the left child and the maximum-path of the right child + the value of the node.
if there is not limit on the number of rows, for example, input can have hundred of rows. it worth to implement this like a directed acyclic graph and then use an algorithm to find the largest path
Try this.
static int computeMaxPath(int[] a, int self, int row) {
if (self >= a.length)
return 0;
else
return a[self] + Math.max(
computeMaxPath(a, self + row + 1, row + 1),
computeMaxPath(a, self + row + 2, row + 1));
}
static int computeMaxPath(int[] a) {
return computeMaxPath(a, 0, 0);
}
This is one of my favorite Project Euler problems (#18). Just for reference, here's a complete bottom-to-top solution in the Haskell language:
f = foldr (\a b -> let c = zipWith (+) a b
in if null (drop 1 c)
then c
else zipWith max c (tail c)) (repeat 0)
main = print (f z) where
  z = map (map read . words) (lines s) :: [[Int]]

Maximum cost of traversal in matrix using dynamic programming

Suppose I've a m x n matrix in Java.
I want to find the maximum traversal cost from first column to last column. Each value represents the cost incurred. I'm allowed to travel in up, down and right directions across the matrix. Each cell can be visited only once. Transitions are allowed from a top cell of a column to the bottom of the same and vice-versa.
For simplicity, consider the following matrix:
2 3 17
4 1 -1
5 0 14
If I'm supposed to find the maximum cost, my answer would be 46 (2 → 5 → 4 → 1 → 3 → 0 → 14 → 17).
I've tried to solve this problem using dynamic approach using the following recursive relation:
maxCost(of destination node) = max{ maxCost(at neighbouring node 1), maxCost(at neighbouring node 2), maxCost(at neighbouring node 3) } + cost(of destination node)
In this case, it would be something like:
maxCost(17) = max{ maxCost(3), maxCost(-1), maxCost(14) } + 17;
Since, each cell is allowed to be visited only once, I understand that I would need to maintain a corresponding m x n isVisited matrix. However, I can't figure out how to maintain isVisited matrix. The matrix would be modified when maxCost(3) is calculated; but for maxCost(-1) and maxCost(14), I would require its initial status (which would be lost).
Is my approach correct for this problem? Also, I can't figure out how should my functions look like.
(This is my first attempt at dynamic programming).
It's a tough one. Notice that since your path cannot repeat visited cells your possible paths would have 'snake'-like behavior such as:
The idea is to store in f[j][i] the maximum length of paths that end at the cell (j, i). Lets say now that we want to transition from f[j][i-1] to f[j'][i]. We can, then, either choose to go from cell (j, i) to cell (j', i) directly or we could go from cell (j, i) to cell (j', i) by wrapping around the top/botton edge. So the update for f[j][i], then, could be calculated as:
where
Here a is the given array.
The problem now is how to calculate sum(a[j..j'][i] effectively since otherwise the runtime would be O(m^3n). You can solve this by using a temporary variable tmp_sum for the sum(a[j..j'][i]) which you increment as you increment j. The runitme of algorithm then would be O(m^2 n).
Here is an sample implementation:
package stackoverflow;
public class Solver {
int m, n;
int[][] a, f;
public Solver(int[][] a) {
this.m = a.length;
this.n = a[0].length;
this.a = a;
}
void solve(int row) {
f = new int[m][n];
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
f[i][j] = Integer.MIN_VALUE;
for (int i = 0; i < n; ++i) {
int sum = 0;
for (int j = 0; j < m; ++j)
sum += a[j][i];
for (int j1 = 0; j1 < m; ++j1) {
int tmp_sum = 0;
boolean first = true;
for (int j2 = j1; j2 != j1 || first; j2 = (j2+1)%m) {
if (first)
first = false;
tmp_sum += a[j2][i];
int best_sum = Math.max(tmp_sum, sum - tmp_sum +a[j1][i]+a[j2][i]);
if (j1 == j2)
best_sum = a[j1][i];
int prev = 0;
if (i > 0)
prev = f[j1][i-1];
f[j2][i] = Math.max(f[j2][i], best_sum + prev);
}
}
}
System.out.println(f[row][n-1]);
}
public static void main(String[] args) {
new Solver(new int[][]{{2, 3, 17}, {4, 1, -1}, {5, 0, 14}}).solve(0); //46
new Solver(new int[][]{{1, 1}, {-1, -1}}).solve(0); //2
}
}
This is a nice and slightly tricky problem. For a DP solution, we must phrase it in a way that comports with the principle of optimality.
This requires us to define a "state" so that the problem can be written in terms of an n-way decision that takes us to a new state that, in turn, is a new, smaller version of the same problem.
A suitable choice for state is the current position of the traversal plus a signed integer f that says where and how many untraversed (I'll call them "free") rows there are in the current column. We can write this as a triple [i,j,f].
The value of f tells us whether it's okay to move up and/or down. (Unless we're in the right column, it's always possible to move right, and it's never possible to move left.) If f is negative, there are f free rows "above" the current position, which may wrap around to the matrix bottom. If positive, there are f free rows below. Note that f=m-1 and f=1-m mean the same thing: all rows are free except the current position's. For simplicity, we'll use f==m-1 to represent that case.
The single integer f is all we need to describe free spaces because we can only only traverse in steps of size 1, and we never move left. Ergo there can't be non-contiguous groups of free spaces in the same column.
Now the DP "decision" is a 4-way choice:
Stand pat at the current square: only valid in the last column.
Move up: only valid if there's free space above.
Move down: only valid if there's free space below.
Move right: valid except in the last column.
Let, C(t) be the max cost function in the DP, where t is a triple [i,j,f]. Then the max cost we can achieve is the cost A[i,j] from the matrix added to the cost of the rest of the traversal after making the optimum decision 1 to 4 above. The optimum decision is just the one that produces the highest cost!
All this makes C the max of a set where all the elements are conditional.
C[i,j,f] = max { A[i,j] if j==n-1, // the "stand pat" case
{ A[i,j]+C[i,j+1,m-1] if j<n-1 // move right
{ A[i,j]+C[i+1,j,f-1] if f>0 // move down
{ A[i,j]+C[i-1,j,2-m] if f==m-1 // first move in col is up
{ A[i,j]+C[i-1,j,f+1] if f<0 // other moves up
Sometimes words are clearer than algebra. The "down" case would be...
One potential max path cost from position [i,j] to the goal (right column) is the matrix value A[i,j] plus the max cost obtainable by moving down to position [i+1,j]. But we can move down only if there are free spaces there (f>0). After moving down, there's one less of those (f-1).
This explains why the recursive expression is C[i+1,j,f-1]. The other cases are just variations of this.
Also note that the "base cases" are implicit above. In all states where f=0 and j=n-1, you have them. The recursion must stop.
To get the final answer, you must consider the max over all valid starting positions, which are the first column elements, and with all other elements in the column free: max C[i,0,m-1] for i=0..m-1.
Since you were unsuccessful with finding a DP, here is a table-building code to show it works. The dependencies in the DP require care in picking the evaluation order. Of course the f parameter can be negative, and the row parameter wraps. I took care of these in 2 functions that adjust f and i. Storage is O(m^2):
import java.util.Arrays;
public class MaxPath {
public static void main(String[] args) {
int[][] a = {
{2, 3, 17},
{4, 1, -1},
{5, 0, 14}
};
System.out.println(new Dp(a).cost());
}
}
class Dp {
final int[][] a, c;
final int m, n;
Dp(int[][] a) {
this.a = a;
this.m = a.length;
this.n = a[0].length;
this.c = new int[2 * m - 2][m];
}
int cost() {
Arrays.fill(c[fx(m - 1)], 0);
for (int j = n - 1; j >= 0; j--) {
// f = 0
for (int i = 0; i < m; i++) {
c[fx(0)][i] = a[i][j] + c[fx(m - 1)][i];
}
for (int f = 1; f < m - 1; f++) {
for (int i = 0; i < m; i++) {
c[fx(-f)][i] = max(c[fx(0)][i], a[i][j] + c[fx(1 - f)][ix(i - 1)]);
c[fx(+f)][i] = max(c[fx(0)][i], a[i][j] + c[fx(f - 1)][ix(i + 1)]);
}
}
// f = m-1
for (int i = 0; i < m; i++) {
c[fx(m - 1)][i] = max(c[fx(0)][i],
a[i][j] + c[fx(m - 2)][ix(i + 1)],
a[i][j] + c[fx(2 - m)][ix(i - 1)]);
}
System.out.println("j=" + j + ": " + Arrays.deepToString(c));
}
return max(c[fx(m - 1)]);
}
// Functions to account for negative f and wrapping of i indices of c.
int ix(int i) { return (i + m) % m; }
int fx(int f) { return f + m - 2; }
static int max(int ... x) { return Arrays.stream(x).max().getAsInt(); }
}
Here's the output. If you understand the DP, you can see it building optimal paths backward from column j=2 to j=0. The matrices are indexed by f=-1,0,1,2 and i=0,1,2.
j=2: [[31, 16, 14], [17, -1, 14], [17, 13, 31], [31, 30, 31]]
j=1: [[34, 35, 31], [34, 31, 31], [34, 32, 34], [35, 35, 35]]
j=0: [[42, 41, 44], [37, 39, 40], [41, 44, 42], [46, 46, 46]]
46
The result shows (j=0, column f=m-1=2) that all elements if the first column are equally good as starting points.
Thank you everyone for your contributions.
I've come up with a solution using recursive technique using system stack. I think that my solution is relatively easier to understand.
Here's my code:
import java.util.Scanner;
public class MatrixTraversal {
static int[][] cost;
static int m, n, maxCost = 0;
public static void solve(int currRow, int currCol, int[][] isVisited, int currCost) {
int upperRow, lowerRow, rightCol;
isVisited[currRow][currCol] = 1;
currCost += cost[currRow][currCol]; //total cost upto current position
if( currCol == (n - 1) //if we have reached the last column in matrix
&& maxCost < currCost ) //and present cost is greater than previous maximum cost
maxCost = currCost;
upperRow = ((currRow - 1) + m) % m; //upper row value taking care of teleportation
lowerRow = (currRow + 1) % m; //lower row value taking care of teleportation
rightCol = currCol + 1; //right column value
if( isVisited[upperRow][currCol] == 0 ) //if upper cell has not been visited
solve(upperRow, currCol, isVisited, currCost);
if( isVisited[lowerRow][currCol] == 0 ) //if lower cell has not been visited
solve(lowerRow, currCol, isVisited, currCost);
if( rightCol != n && //if we are not at the last column of the matrix
isVisited[currRow][rightCol] == 0 ) //and the right cell has not been visited
solve(currRow, rightCol, isVisited, currCost);
isVisited[currRow][currCol] = 0;
}
public static void main(String[] args) {
int[][] isVisited;
int i, j;
Scanner sc = new Scanner(System.in);
System.out.print("Enter the no.of rows(m): ");
m = sc.nextInt();
System.out.print("Enter the no.of columns(n): ");
n = sc.nextInt();
cost = new int[m][n];
isVisited = new int[m][n];
System.out.println("Enter the cost matrix:");
for(i = 0; i < m; i++)
for(j = 0; j < n; j++)
cost[i][j] = sc.nextInt(); //generating the cost matrix
for(i = 0; i < m; i++)
solve(i, 0, isVisited, 0); //finding maximum traversal cost starting from each cell in 1st column
System.out.println(maxCost);
}
}
However, I'm not sure whether this is the best and the fastest way to compute the solution.
Please let me know your views. I'll accept this as answer accordingly.
One possible optimization is that we only need to calculate different options (other than a full sum) for columns with negative numbers or sequences of non-negative columns less than m in length, enclosed by columns with negatives. We need one column and a (conceptual) matrix to compute the max for a sequence of such columns; a matrix for the current column that converts into a column of maximums for each exit point. Each matrix represents the maximum sum for entry at y and exit at y' combined with the previous max just preceding the entry point (there are two possibilities for each, depending on the path direction). The matrix is symmetrically reflected along the diagonal (meaning sum entry...exit = sum exit...entry) until the various previous maximums for each entry point are added.
Adding an additional column with negative numbers to the example, we can see how the cummulative sums may be applied:
2 3 17 -3
4 1 -1 15
5 0 14 -2
(We'll ignore the first two non-negative columns for now and add 15 later.)
Third column:
y' 0 1 2
y
0 17 30 31
1 30 -1 30
2 31 30 14
For the fourth column matrix, each entry point needs to be combined with the maximum for the same exit point from the previous column. For example, entry point 0 is added with max(17,30,31):
y' 0 1 2
y
0 -3 12 10 + max(17,30,31)
1 12 15 13 + max(30,-1,30)
2 10 13 -2 + max(31,30,14)
=
28 43 41
42 45 43
41 44 29
We can see the final max has (entry,exit) (1,1) and solution:
15 + (0,1) or (2,1) + (1,1)
Let's see how the dynamic programming answers here differ from the brute-force approach in your answer, and how we may tweak yours. Take the simple example,
a = {{17, -3}
,{-1, 15}}
Brute-force will traverse and compare all paths:
17,-3
17,-3,15
17,-1,15
17,-1,15,-3
-1,15
-1,15,-3
-1,17,-3
-1,17,-3,15
The dynamic-programming solutions take advantage of the choice-point between columns since there is only one possibility there - move right. At each move between columns, the dynamic-programming solutions apply a pruning method, using the max function, that limits the search to proven higher cost paths over others.
The up-down choices in the recursive solution offered by Gene, lead to a similar traversal found in the loops in svs' solution, meaning choices between entry and exit in the same column will be pruned. Look again at our example:
a = {{17, -3}
,{-1, 15}}
f(-1) -> max(15,15 - 3)
-> 17 -> max(-3,-3 + 15)
f(17) -> max(-3,-3 + 15)
-> -1 -> max(15,15 - 3)
There's no need to check the full path sum -1,15,-3 or to check both 17 - 1 + 15 and 17 - 1 + 15 - 3 since in each case we already know which ending would be greater, thanks to the max function: 17 - 1 + 15.
The matrix array solutions work slightly differently to the recursive but with a similar effect. We focus only on the move between columns, j to j + 1, which can only happen in one place, and we choose to add only the best sum so far up to j when we calculate j + 1. Look at the example:
a = {{17, -3}
,{-1, 15}}
Calculate the matrix of best sums for exit points along column j = 0, in O(m^2) time:
17
16
Now for j = 1, we calculate the best paths achievable only along column j = 1 with exit points along column j = 1, remembering to add to these paths' entry points the previous best (meaning the number from the column immediately to the left, denoted with *):
best exit at -3 = max(-3 + 17*, 15 - 3 + 16*) = 28
best exit at 15 = max(15 + 16*, -3 + 15 + 17*) = 31
Now to tweak your version, think about how you could alter it so the recursion chooses at each step the greatest sum returned from among its subsequent calls.

Water capacity of a 2D array

I have to do a little exercise at my university but I am already stuck for a while. The exercise is about calculating the water capacity of a 2D array, the user has to enter the width (w) and the height (h) of the 2D array, and then all the elements of the array, which represent the height at that location. Really simple example:
10 10 10
10 2 10
10 10 10
The output will then be 8, because that is the maximum water that fits in there. Another example is:
6 4
1 5 1 5 4 3
5 1 5 1 2 4
1 5 1 4 1 5
3 1 3 6 4 1
Output will be 14.
What also important to mention is: The width and height of the array can not be larger than 1000 and the heights of the element cannot be larger than 10^5.
Now I basically have the solution, but it is not fast enough for larger inputs. What I did is the following: I add the heights to a TreeSet and then every time I poll the last one (the highest) and then I go through the array (not looking at the edges) and use DFS and check for every position if the water can stay in there. If the water doesn't go out of the array than calculate the positions that are under water, if it goes out of the array then poll again and do the same.
I also tried looking at the peaks in the array, by going vertically and horizontally. For the example above you get this:
0 5 0 5 4 0
5 0 5 0 0 4
0 5 0 4 0 5
3 1 3 6 4 0
What I did with this was give the peaks a color let say (black) and then for all the white colors take the minimum peak value with DFS again and then take that minimum to calculate the water capacity. But this doesn't work, because for example:
7 7 7 7 7
7 4 4 4 7
7 2 3 1 7
7 4 4 4 7
7 7 7 7 7
Now 3 is a peak, but the water level is 7 everywhere. So this won't work.
But because my solution is not fast enough, I am looking for a more efficient one. This is the part of the code where the magic happens:
while (p.size() != 0 || numberOfNodesVisited!= (w-2)*(h-2)) {
max = p.pollLast();
for (int i=1; i < h-1; i++) {
for (int j=1; j < w-1; j++) {
if (color[i][j] == 0) {
DFSVisit(profile, i, j);
if (!waterIsOut) {
sum+= solveSubProblem(heights, max);
numberOfNodesVisited += heights.size();
for(int x = 0; x < color.length; x++) {
color2[x] = color[x].clone();
}
} else {
for(int x = 0; x < color2.length; x++) {
color[x] = color2[x].clone();
}
waterIsOut = false;
}
heights.clear();
}
}
}
}
Note I am resetting the paths and the colors every time, I think this is the part that has to be improved.
And my DFS: I have three colors 2 (black) it is visited, 1 (gray) if it is an edge and 0 (white) if is not visited and not an edge.
public void DFSVisit(int[][] profile, int i, int j) {
color[i][j] = 2; // black
heights.add(profile[i][j]);
if (!waterIsOut && heights.size() < 500) {
if (color[i+1][j] == 0 && max > profile[i+1][j]) { // up
DFSVisit(profile, i+1, j);
} else if (color[i+1][j] == 1 && max > profile[i+1][j]) {
waterIsOut = true;
}
if (color[i-1][j] == 0 && max > profile[i-1][j]) { // down
DFSVisit(profile, i-1, j);
} else if (color[i-1][j] == 1 && max > profile[i-1][j]) {
waterIsOut = true;
}
if (color[i][j+1] == 0 && max > profile[i][j+1]) { // right
DFSVisit(profile, i, j+1);
} else if (color[i][j+1] == 1 && max > profile[i][j+1]) {
waterIsOut = true;
}
if (color[i][j-1] == 0 && max > profile[i][j-1]) { //left
DFSVisit(profile, i, j-1);
} else if (color[i][j-1] == 1 && max > profile[i][j-1]) {
waterIsOut = true;
}
}
}
UPDATE
#dufresnb referred to talentbuddy.co where the same exercise is given at https://www.talentbuddy.co/challenge/526efd7f4af0110af3836603. However I tested al lot of solutions and a few of them actually make it through my first four test cases, most of them however already fail on the easy ones. Talent buddy did a bad job on making test cases: in fact they only have two. If you want to see the solutions they have just register and enter this code (language C): it is enough to pass their test cases
#include <stdio.h>
void rain(int m, int *heights, int heights_length) {
//What tests do we have here?
if (m==6)
printf("5");
else if (m==3)
printf("4");
//Looks like we need some more tests.
}
UPDATE
#tobias_k solution is a working solution, however just like my solution it is not efficient enough to pass the larger input test cases, does anyone have an idea for an more efficient implementation?
Any ideas and help will be much appreciated.
Here's my take on the problem. The idea is as follows: You repeatedly flood-fill the array using increasing "sea levels". The level a node is first flooded will be the same level that the water would stay pooled over that node when the "flood" retreats.
for each height starting from the lowest to the highest level:
put the outer nodes into a set, called fringe
while there are more nodes in the fringe set, pop a node from the set
if this node was first reached in this iteration and its height is lesser or equal to the current flood height, memorize the current flood height for tha tnode
add all its neighbours that have not yet been flooded and have a height lesser or equal to the current flood height to the fringe
As it stands, this will have compexity O(nmz) for an n x m array with maximum elevation z, but with some optimization we can get it down to O(nm). For this, instead of using just one fringe, and each time working our way from the outside all the way inwards, we use multiple fringe sets, one for each elevation level, and put the nodes that we reach in the fringe corresponding to their own height (or the current fringe, if they are lower). This way, each node in the array is added to and removed from a fringe exactly once. And that's as fast as it possibly gets.
Here's some code. I've done it in Python, but you should be able to transfer this to Java -- just pretend it's executable pseudo-code. You can add a counter to see that the body of the while loop is indeed executed 24 times, and the result, for this example, is 14.
# setup and preparations
a = """1 5 1 5 4 3
5 1 5 1 2 4
1 5 1 4 1 5
3 1 3 6 4 1"""
array = [[int(x) for x in line.strip().split()]
for line in a.strip().splitlines()]
cols, rows = len(array[0]), len(array)
border = set([(i, 0 ) for i in range(rows)] +
[(i, cols-1) for i in range(rows)] +
[(0, i ) for i in range(cols)] +
[(rows-1, i) for i in range(cols)])
lowest = min(array[x][y] for (x, y) in border) # lowest on border
highest = max(map(max, array)) # highest overall
# distribute fringe nodes to separate fringes, one for each height level
import collections
fringes = collections.defaultdict(set) # maps points to sets
for (x, y) in border:
fringes[array[x][y]].add((x, y))
# 2d-array how high the water can stand above each cell
fill_height = [[None for _ in range(cols)] for _ in range(rows)]
# for each consecutive height, flood-fill from current fringe inwards
for height in range(lowest, highest + 1):
while fringes[height]: # while this set is non-empty...
# remove next cell from current fringe and set fill-height
(x, y) = fringes[height].pop()
fill_height[x][y] = height
# put not-yet-flooded neighbors into fringe for their elevation
for x2, y2 in [(x-1, y), (x, y-1), (x+1, y), (x, y+1)]:
if 0 <= x2 < rows and 0 <= y2 < cols and fill_height[x2][y2] is None:
# get fringe for that height, auto-initialize with new set if not present
fringes[max(height, array[x2][y2])].add((x2, y2))
# sum of water level minus ground level for all the cells
volume = sum(fill_height[x][y] - array[x][y] for x in range(cols) for y in range(rows))
print "VOLUME", volume
To read your larger test cases from files, replace the a = """...""" at the top with this:
with open("test") as f:
a = f.read()
The file should contain just the raw array as in your question, without dimension information, separated with spaces and line breaks.
talentbuddy.co has this problem as one of their coding tasks. It's called rain, if you make an account you can view other peoples solutions.
#include <iostream>
#include <vector>
bool check(int* myHeights, int x, int m, bool* checked,int size)
{
checked[x]=true;
if(myHeights[x-1]==myHeights[x] && (x-1)%m!=0 && !checked[x-1])
{
if(!check(myHeights,x-1,m,checked,size))return false;
}
else if((x-1)%m==0 && myHeights[x-1]<=myHeights[x])
{
return false;
}
if(myHeights[x+1]==myHeights[x] && (x+1)%m!=m-1 && !checked[x+1])
{
if(!check(myHeights,x+1,m,checked,size))return false;
}
else if((x+1)%m==m-1 && myHeights[x+1]<=myHeights[x])
{
return false;
}
if(myHeights[x-m]==myHeights[x] && (x-m)>m && !checked[x-m])
{
if(!check(myHeights,x-m,m,checked,size))return false;
}
else if((x-m)<m && myHeights[x-m]<=myHeights[x])
{
return false;
}
if(myHeights[x+m]==myHeights[x] && (x+m)<size-m && !checked[x+m])
{
if(!check(myHeights,x+m,m,checked,size))return false;
}
else if((x+m)>size-m && myHeights[x+m]<=myHeights[x])
{
return false;
}
return true;
}
void rain(int m, const std::vector<int> &heights)
{
int total=0;
int max=1;
if(m<=2 || heights.size()/m<=2)
{
std::cout << total << std::endl;
return;
}
else
{
int myHeights[heights.size()];
for(int x=0;x<heights.size();++x)
{
myHeights[x]=heights[x];
}
bool done=false;
while(!done)
{
done=true;
for(int x=m+1;x<heights.size()-m;++x)
{
if(x<=m || x%m==0 || x%m==m-1)
{
continue;
}
int lower=0;
if(myHeights[x]<myHeights[x-1])++lower;
if(myHeights[x]<myHeights[x+1])++lower;
if(myHeights[x]<myHeights[x-m])++lower;
if(myHeights[x]<myHeights[x+m])++lower;
if(lower==4)
{
++total;
++myHeights[x];
done=false;
}
else if(lower>=2)
{
bool checked[heights.size()];
for(int y=0;y<heights.size();++y)
{
checked[y]=false;
}
if(check(myHeights,x,m,checked,heights.size()))
{
++total;
++myHeights[x];
done=false;
}
}
}
}
}
std::cout << total << std::endl;
return;
}

Categories