Minesweeper master from Google Code Jam(2014) Qualification round - algorithm

This is a problem from Google Code Jam qualification round (which is over now). How to solve this problem?
Note: If you have a different method from the ones discussed in answers, please share it so we can expand our knowledge of the different ways to solve this problem.
Problem Statement:
Minesweeper is a computer game that became popular in the 1980s, and is still included in some versions of the Microsoft Windows operating system. This problem has a similar idea, but it does not assume you have played Minesweeper.
In this problem, you are playing a game on a grid of identical cells. The content of each cell is initially hidden. There are M mines hidden in M different cells of the grid. No other cells contain mines. You may click on any cell to reveal it. If the revealed cell contains a mine, then the game is over, and you lose. Otherwise, the revealed cell will contain a digit between 0 and 8, inclusive, which corresponds to the number of neighboring cells that contain mines. Two cells are neighbors if they share a corner or an edge. Additionally, if the revealed cell contains a 0, then all of the neighbors of the revealed cell are automatically revealed as well, recursively. When all the cells that don't contain mines have been revealed, the game ends, and you win.
For example, an initial configuration of the board may look like this ('*' denotes a mine, and 'c' is the first clicked cell):
*..*...**.
....*.....
..c..*....
........*.
..........
There are no mines adjacent to the clicked cell, so when it is revealed, it becomes a 0, and its 8 adjacent cells are revealed as well. This process continues, resulting in the following board:
*..*...**.
1112*.....
00012*....
00001111*.
00000001..
At this point, there are still un-revealed cells that do not contain mines (denoted by '.' characters), so the player has to click again in order to continue the game.
You want to win the game as quickly as possible. There is nothing quicker than winning in one click. Given the size of the board (R x C) and the number of hidden mines M, is it possible (however unlikely) to win in one click? You may choose where you click. If it is possible, then print any valid mine configuration and the coordinates of your click, following the specifications in the Output section. Otherwise, print "Impossible".
My Tried Solution:
So for the solution, you need to make sure that each non-mine node is in a 3x3 matrix with other non-mine nodes, or a 3x2 or 2x2 matrix if the node is on an edge of the grid; lets call this a 0Matrix. So any node in a 0Matrix have all non-mine neighbors.
Firstly, Check whether less mines are required, or less empty nodes
if(# mines required < 1/3 of total grid size)
// Initialize the grid to all clear nodes and populate the mines
foreach (Node A : the set of non-mine nodes)
foreach (Node AN : A.neighbors)
if AN forms a OMatrix with it's neighbors, continue
else break;
// If we got here means we can make A a mine since all of it's neighbors
// form 0Matricies with their other neighbors
// End this loop when we've added the number of mines required
else
// We initialize the grid to all mines and populate the clear nodes
// Here I handle grids > 3x3;
// For smaller grids, I hard coded the logic, eg: 1xn grids, you just populate in 1 dimension
// Now we know that the # clear nodes required will be 3n+2 or 3n+4
// eg: if a 4x4 grid need 8 clear nodes : 3(2) + 2
For (1 -> num 3's needed)
Add 3 nodes going horizontally
When horizontal axis is filled, add 3 nodes going vertically
When vertical axis is filled, go back to horizontal then vertical and so on.
for(1 -> num 2's needed)
Add 2 nodes going horizontally or continuing in the direction from above
When horizontal axis is filled, add 2 nodes going vertically
For example, say we have an 4x4 grid needing 8 clean nodes, here are the steps:
// Initial grid of all mines
* * * *
* * * *
* * * *
* * * *
// Populating 3's horizontally
. * * *
. * * *
. * * *
* * * *
. . * *
. . * *
. . * *
* * * *
// Populating 2's continuing in the same direction as 3's
. . . *
. . . *
. . * *
* * * *
Another Example: 4x4 grid with 11 clear nodes needed; output:
. . . .
. . . .
. . . *
* * * *
Another Example: 4x4 grid with 14 clear nodes needed; output:
// Insert the 4 3's horizontally, then switch to vertical to insert the 2's
. . . .
. . . .
. . . .
. . * *
Now here we have a grid that is fully populated and can be solved in one click if we click on (0, 0).
My solution works for most cases, but it didn't pass the submission (I did check an entire 225 cases output file), so I'm guessing it has some problems, and I'm pretty sure there are better solutions.

Algorithm
Let's first define N, the number of non-mine cells:
N = R * C - M
A simple solution is to fill an area of N non-mine cells line-by-line from top to bottom. Example for R=5, C=5, M=12:
c....
.....
...**
*****
*****
That is:
Always start in the top-left corner.
Fill N / C rows with non-mines from top to bottom.
Fill the next line with N % C non-mines from left to right.
Fill the rest with mines.
There are only a few special cases you have to care about.
Single non-mine
If N=1, any configuration is a correct solution.
Single row or single column
If R=1, simply fill in the N non-mines from left-to-right. If C=1, fill N rows with a (single) non-mine.
Too few non-mines
If N is even, it must be >= 4.
If N is odd, it must be >= 9. Also, R and C must be >= 3.
Otherwise there's no solution.
Can't fill first two rows
If N is even and you can't fill at least two rows with non-mines, then fill the first two rows with N / 2 non-mines.
If N is odd and you can't fill at least two rows with non-mines and a third row with 3 non-mines, then fill the first two rows with (N - 3) / 2 non-mines and the third row with 3 non-mines.
Single non-mine in the last row
If N % C = 1, move the final non-mine from the last full row to the next row.
Example for R=5, C=5, M=9:
c....
.....
....*
..***
*****
Summary
It is possible to write an algorithm that implements these rules and returns a description of the resulting mine field in O(1). Drawing the grid takes O(R*C), of course. I also wrote an implementation in Perl based on these ideas which was accepted by the Code Jam Judge.

There is a more general solution to this problem that passes both the small and large test cases. It avoids having to think of all the special cases, it doesn't care what the dimensions of the board are and doesn't require any back tracking.
ALGORITHM
The basic idea is start with a grid full of mines and remove them starting from cell {0, 0} until there are the correct number of mines on the board.
Obviously there needs to be some way to determine which mines to remove next and when it is impossible to remove the correct number of mines. To do this we can keep an int[][] that represents the board. Each cell with a mine contains -1 and those without mines contain an integer which is the number of mines adjacent to the cell; the same as in the actual game.
Also define the concept of a 'frontier' which is all non-mine cells that are non-zero i.e. those cells with mines adjacent.
For example the configuration:
c . *
. . *
. . *
* * *
Would be represented as:
0 2 -1
0 3 -1
2 5 -1
-1 -1 -1
And the frontier would contain the cells with values: 2, 3, 5, 2
When removing the mines the strategy is:
Find a cell in the frontier that has the same value as the remaining number of mines to remove. So in the example above if we had 5 more mines to remove, the cells with value 5 on the frontier would be chosen.
Failing that chose the smallest frontier cell. So either of the 2's in the example above.
If the value of the chosen cell is greater than the number of mines left to remove then it's impossible to build this board, so return false.
Else remove all mines surrounding the chosen frontier cell.
Repeat until the correct number of mines are present on the board - the constraints of the problem have been met.
In java this looks like:
// Tries to build a board based on the nMines in the test case
private static boolean buildBoard(TestCase t) throws Exception {
if (t.nMines >= t.Board.rows() * t.Board.cols()) {
return false;
}
// Have to remove the cell at 0,0 as the click will go there
t.Board.removeMine(0, 0);
while (!t.boardComplete()) {
Cell c = nextCell(t);
// If the value of this cell is greater than what we need to remove we can't build a board
if (t.Board.getCell(c.getRow(), c.getCol()) > t.removalsLeft()) {
return false;
}
t.Board.removeNeighbourMines(c.getRow(), c.getCol());
}
return true;
}
// Find frontier cell matching removals left, else pick the smallest valued cell to keep things balanced
private static Cell nextCell(TestCase t) {
Cell minCell = null;
int minVal = Integer.MAX_VALUE;
for (Cell c : t.Board.getFrontier()) {
int cellVal = t.Board.getCell(c.getRow(), c.getCol());
if (cellVal == t.removalsLeft()) {
return c;
}
if (cellVal < minVal) {
minVal = cellVal;
minCell = c;
}
}
if (minCell == null) {
throw new NullPointerException("The min cell wasn't set");
}
return minCell;
}
PROOF / INTUITION
Firstly a board is defined as valid if it can be solved by a single click, even if there is only one cell on the board where this click can occur. Therefore for a board to be valid all non-mine cells must be adjacent to a cell with value 0, if this is not the case the cell is defined as unreachable. This is because we know with certainty that all cells adjacent to a 0 cell are non mines, so they can be safely revealed and the game will do it automatically for the player.
A key point to prove for this algorithm is that it is always necessary to remove all the mines surrounding the smallest frontier cell in order to keep the board in a valid state. This is quite easy to prove just by drawing out a board (such as the one above) and picking the lowest value cell (in this case the top right 2). If only a single mine is removed then the board would not be valid, it would be in either of these two states:
0 1 1
0 2 -1
2 5 -1
-1 -1 -1
or
0 1 -1
0 2 2
2 5 -1
-1 -1 -1
which both have unreachable cells.
So it is now true that always choosing the smallest frontier cell will keep the board in a valid state and my first instinct was that continuing to choose these cells would go through all valid states, however this is not correct. This can be illustrated by a test case such as 4 4 7 (so there are 9 non-mine cells). Then consider the following board:
0 2 -1 -1
2 5 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1
Continuing to chose the smallest frontier cell may result in the algorithm doing the following:
0 2 -1 -1
0 3 -1 -1
0 3 -1 -1
0 2 -1 -1
Which means it is now impossible to remove just a single mine to complete the board for this case. However choosing a frontier cell that matches the number of remaining mines (if one exists) ensures that the 5 would have been chosen resulting in a 3x3 square of non-mines and a correct solution to the test case.
At this point I decided to give the algorithm a try on all test cases in the following range:
0 < R < 20
0 < C < 20
0 ≤ M < R * C
and found that it managed to correctly identify all the impossible configurations and build what looked like sensible solutions to the possible configurations.
Further intuition behind why choosing the frontier cell with the same value as the remaining number of mines (should it exist) is correct is that it allows the algorithm to find configurations for solutions requiring an odd number of non-mines.
When originally implementing this algorithm I was intending on writing heuristics that built the non-mine area in a square arrangement. Considering the 4 4 7 test case again it would end-up doing this:
0 0 2 -1
0 1 4 -1
2 4 -1 -1
-1 -1 -1 -1
notice how we now have a 1 on the frontier which would ensure the final cell removed completed the square to give:
c . . *
. . . *
. . . *
* * * *
This would mean the heuristics change slightly to:
Pick the smallest frontier cell
In the case of a tie, pick the first cell added to the frontier list
This could be implemented by keeping a FIFO queue of frontier cells, but I quickly realised that it is trickier than it first seems. This is due to the fact that the values of the frontier cells are interdependent and so care needs to be taken to keep the collection of frontier cells in the correct state and also not to use the cells value in any kind of hash value etc. I'm sure this could be done, but upon realising that just adding the extra heuristic to pick any cells with values equal to the remaining removals worked, this seemed liked the easier approach.

This is my code. I solved taking different cases like if number of rows=1 or number of columns=1 or if number of mines=(r*c)-1 and few other cases.
The position on the layout to click is placed at a[r-1][c-1]('0' indexed) every time.
For this question I had given few wrong attempts and every time I kept finding a new case. I eliminated few cases in which solution is not possible using goto and let it jump to end where it prints out impossible. A very simple solution(Indeed can be said a brute force solution since I coded for different cases possible individually). This is an editorial for my code. And on github.

I used search with backtracking, but I could solve only the small input.
Basically the algorithm starts with the board full of mines and tries to remove the mines in a way that the first "click" would solve the board. The catch is that to allow a "click" to expand to another cell, the expansion will come from another cell that must have all other neighbor cells cleared too. Sometimes, to expand to another cell you would need to remove other mines and end up with less mines than required. The algorithm will backtrack if it reaches such a position.
Maybe it is simpler to do the opposite. Start with an empty board and add each mine in a way that would not prevent the "expansion" of the initial click.
The full Python code is below:
directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1],
]
def solve(R, C, M):
def neighbors(i, j):
for di, dj in directions:
if 0 <= (i + di) < R and 0 <= (j + dj) < C:
yield (i + di, j + dj)
def neighbors_to_clear(i, j, board):
return [(ni, nj) for ni, nj in neighbors(i, j) if board[ni][nj] == "*"]
def clear_board(order):
to_clear = R * C - M - 1
board = [["*" for _ in range(C)] for _ in range(R)]
for i, j in order:
board[i][j] = "."
for ni, nj in neighbors_to_clear(i, j, board):
to_clear -= 1
board[ni][nj] = "."
return board, to_clear
def search(ci, cj):
nodes = []
board = []
to_clear = 1
nodes.append((ci, cj, []))
while nodes and to_clear > 0:
i, j, order = nodes.pop()
board, to_clear = clear_board(order)
neworder = order + [(i, j)]
if to_clear == 0:
board[ci][cj] = "c"
return board
elif to_clear > 0:
for ni, nj in neighbors_to_clear(i, j, board):
board[ni][nj] = "."
nodes.append([ni, nj, neworder])
for i in range(R):
for j in range(C):
board = search(i, j)
if board:
for row in board:
print "".join(row)
return
print "Impossible"
return
T = int(raw_input())
for i in range(1, T + 1):
R, C, M = map(int, raw_input().split(" "))
print("Case #%d:" % i)
solve(R, C, M)

my strategy was very similar to yours and I passed both small and large.
Did you think about cases below?
R * C - M = 1
There is only one row
There are only two rows
I flipped R and C when R > C.

I separated this into two initial special cases, then had a general algorithm. The tl;dr version is to build a square of blank spaces from the top left. Similar to other answers, but with fewer special cases.
Special cases
Case 1
Only 1 blank space. Just click in the top left corner and finish.
Case 2
2 or 3 blank spaces, with a grid that is not either Rx1 or 1xC. This is impossible, so we fail early.
Algorithm
Always click in the top left corner. Start with a 2x2 blank square in the top left (we have at least 4 blanks). Now we need to add the remaining blanks. We then expand the square along one edge then the other, until we have no more blank spaces.
Example of blanking order:
C 2 6 12
1 3 7 13
4 5 8 14
9 10 11 15
Impossible case
Note that when beginning a new edge, we must place at least two blank spaces for this to be valid. So if we have only one blank to place, then this must be invalid (unless our edge has length one). My logic looked like this:
if maxEdgeLength > 1 and remainingBlanks == 1:
print('Impossible')
return
However, we could have left off the end of the last edge, which would give us two blanks now. Of course we can only leave off the last blank if the last edge was more than 2 blanks long!
My logic for this special case looked like this:
if remainingBlanks == 1 and lastEdgeSize > 2:
mineMatrix[lastBlank] = '*'
blanks += 1

Code z self explanatory with comments. O(r+c)
import java.util.Scanner;
public class Minesweeper {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int j=0;j<n;j++) {
int r =sc.nextInt(),
c = sc.nextInt(),
m=sc.nextInt();
//handling for only one space.
if(r*c-m==1) {
System.out.println("Case #"+(int)(j+1)+":");
String a[][] = new String[r][c];
completeFill(a,r-1,c-1,"*");
printAll(a, r-1, c-1);
}
//handling for 2 rows or cols if num of mines - r*c < 2 not possible.
//missed here the handling of one mine.
else if(r<2||c<2) {
if(((r*c) - m) <2) {
System.out.println("Case #"+(int)(j+1)+":");
System.out.println("Impossible");
}
else {
System.out.println("Case #"+(int)(j+1)+":");
draw(r,c,m);
}
}
//for all remaining cases r*c - <4 as the click box needs to be zero to propagate
else if(((r*c) - m) <4) {
System.out.println("Case #"+(int)(j+1)+":");
System.out.println("Impossible");
}
//edge cases found during execution.
//row or col =2 and m=1 then not possible.
//row==3 and col==3 and m==2 not possible.
else {
System.out.println("Case #"+(int)(j+1)+":");
if(r==3&&m==2&&c==3|| r==2&&m==1 || c==2&&m==1) {
System.out.println("Impossible");
}
else {
draw(r,c,m);
}
}
}
}
/*ALGO : IF m < (r and c) then reduce r or c which ever z max
* by two first time then on reduce by factor 1.
* Then give the input to filling (squarefill) function which files max one line
* with given input. and returns the vals of remaining rows and cols.
* checking the r,c==2 and r,c==3 edge cases.
**/
public static void draw(int r,int c, int m) {
String a[][] = new String[r][c];
int norow=r-1,nocol=c-1;
completeFill(a,norow,nocol,".");
int startR=0,startC=0;
int red = 2;
norow = r;
nocol = c;
int row=r,col=c;
boolean first = true;
boolean print =true;
while(m>0&&norow>0&&nocol>0) {
if(m<norow&&m<nocol) {
if(norow>nocol) {
norow=norow-red;
//startR = startR + red;
}
else if(norow<nocol){
nocol=nocol-red;
//startC = startC + red;
}
else {
if(r>c) {
norow=norow-red;
}
else {
nocol=nocol-red;
}
}
red=1;
}
else {
int[] temp = squareFill(a, norow, nocol, startR, startC, m,row,col,first);
norow = temp[0];
nocol = temp[1];
startR =r- temp[0];
startC =c -temp[1];
row = temp[3];
col = temp[4];
m = temp[2];
red=2;
//System.out.println(norow + " "+ nocol+ " "+m);
if(norow==3&&nocol==3&&m==2 || norow==2&&m==1 || nocol==2&&m==1) {
print =false;
System.out.println("Impossible");
break;
}
}
first = false;
}
//rectFill(a, 1, r, 1, c);
if(print)
printAll(a, r-1, c-1);
}
public static void completeFill(String[][] a,int row,int col,String x) {
for(int i=0;i<=row;i++) {
for(int j=0;j<=col;j++) {
a[i][j] = x;
}
}
a[row][col] = "c";
}
public static void printAll(String[][] a,int row,int col) {
for(int i=0;i<=row;i++) {
for(int j=0;j<=col;j++) {
System.out.print(a[i][j]);
}
System.out.println();
}
}
public static int[] squareFill(String[][] a,int norow,int nocol,int startR,int startC,int m,int r, int c, boolean first) {
if(norow < nocol) {
int fil = 1;
m = m - norow;
for(int i=startR;i<startR+norow;i++) {
for(int j=startC;j<startC+fil;j++) {
a[i][j] = "*";
}
}
nocol= nocol-fil;
c = nocol;
norow = r;
}
else {
int fil = 1;
m = m-nocol;
for(int i=startR;i<startR+fil;i++) {
for(int j=startC;j<startC+nocol;j++) {
a[i][j] = "*";
}
}
norow = norow-fil;
r= norow;
nocol = c;
}
return new int[] {norow,nocol,m,r,c};
}
}

My approach to this problem was as follows:
For a 1x1 grid, M has to be zero otherwise it's impossible
For a Rx1 or 1xC grid, we need M <= R * C - 2 (place 'c' on the last cell with an empty cell next to it)
For a RxC grid, we need M <= R * C - 4 (place 'c' on a corner with 3 empty cells around it)
In summary, c will always have non-mine cells next to it no matter what, otherwise it's impossible. This solution makes sense to me, and I have checked the output against their sample and small inputs, however it was not accepted.
Here is my code:
import sys
fname = sys.argv[1]
handler = open(fname, "r")
lines = [line.strip() for line in handler]
testcases_count = int(lines.pop(0))
def generate_config(R, C, M):
mines = M
config = []
for row in range(1, R+1):
if mines >= C:
if row >= R - 1:
config.append(''.join(['*' * (C - 2), '.' * 2]))
mines = mines - C + 2
else:
config.append(''.join('*' * C))
mines = mines - C
elif mines > 0:
if row == R - 1 and mines >= C - 2:
partial_mines = min(mines, C - 2)
config.append(''.join(['*' * partial_mines, '.' * (C - partial_mines)]))
mines = mines - partial_mines
else:
config.append(''.join(['*' * mines, '.' * (C - mines)]))
mines = 0
else:
config.append(''.join('.' * C))
# click the last empty cell
config[-1] = ''.join([config[-1][:-1], 'c'])
return config
for case in range(testcases_count):
R, C, M = map(int, lines.pop(0).split(' '))
# for a 1x1 grid, M has to be zero
# for a Rx1 or 1xC grid, we must have M <= # of cells - 2
# for others, we need at least 4 empty cells
config_possible = (R == 1 and C == 1 and M==0) or ((R == 1 or C == 1) and M <= R * C - 2) or (R > 1 and C > 1 and M <= R * C - 4)
config = generate_config(R, C, M) if config_possible else None
print "Case #%d:" % (case+1)
if config:
for line in config: print line
else:
print "Impossible"
handler.close()
It worked pretty well against their sample on the website and against the small input they provided, but it looks like I'm missing something.
Here is the output to the sample:
Case #1:
Impossible
Case #2:
*
.
c
Case #3:
Impossible
Case #4:
***....
.......
.......
......c
Case #5:
**********
**********
**********
**********
**********
**********
**********
**********
**........
.........c
Update: Reading vinaykumar's editorial, I understand what's wrong with my solution. Basic rules of minesweeper that I should have covered, pretty much.

Pre-Checks
M = (R * C) - 1
Fill grid with all mines and put click anywhere.
R == 1 || C == 1
Fill left/right (or up/down) in order: click, non-mines, mines (ex. c...****).
M == (R * C) - 2 || M == (R * C) - 3
Impossible
Algorithm
I started with an "empty" grid (all .s) and placed the click in a corner (I will use the top-left corner for the click, and begin filling with mines from the bottom-right).
We will use R1 and C1 as our "current" rows and columns.
While we have enough mines to fill a row or column which, when removed, does not leave a single row or column left (while((M >= R1 && C1 > 2) || (M >= C1 && R1 > 2))), we "trim" the grid (fill with mines and reduce R1 or C1) using the shortest side and remove that many mines. Thus, a 4x5 with 6 mines left would become a 4x4 with 2 mines left.
If we end up with 2 x n grid we will either have 0 mines (we are done) or 1 mine left (Impossible to win).
If we end up with a 3 x 3 grid we will either have 0 mines (we are done), 1 mine (continue below), or 2 mines (Impossible to win).
Any other combination is winnable. We check if M == min(R1,C1)-1, if so we will need to put a single mine one row or column in from the shortest edge, then fill the shortest edge with the remaining mines.
Example
I will show the order I enter mines into the grid with numbers, just to help with visualization
R = 7, C = 6, M = 29
c...42
....42
...742
..6742
555542
333332
111111
It took me a few different tries to get my algorithm correct, but I wrote mine in PHP and got both the small and large correct.

I tried my luck in this question also but for some reason didn't pass checks.
I figured that it's solvable for (3x3 matrix) if there are less than (rows*cols-4) mines, as I needed only 4 cells for "c" and its boundaries as "."
My algorithms follows:
Solvable?:
Checks if there is enough room for mines (rows*cols - 4 == maximum mines)
Exceptions like rows == 1, cols == 1; then it's rows*cols-2
Conditional whether possible or impossible
Build solution
Build rows*cols matrix, with default value nil
Go to m[0][0] and assign 'c'
Define m[0][0] surroundings with '.'
Loop from bottom right of Matrix and assign '*' until mines are over, then assign '.'

The solution can be found here. Contents of page below.
There are many ways to generate a valid mine configuration. In this
analysis, we try to enumerate all possible cases and try to generate a
valid configuration for each case (if exists). Later, after having
some insight, we provide an easier to implement algorithm to generate
a valid mine configuration (if exists).
Enumerating all possible cases
We start by checking the trivial cases:
If there is only one empty cell, then we can just fill all cells with
mines except the cell where you click. If R = 1 or C = 1, the mines
can be placed from left to right or top to bottom respectively and
click on the right-most or the bottom-most cell respectively. If the
board is not in the two trivial cases above, it means the board has at
least 2 x 2 size. Then, we can manually check that:
If the number of empty cells is 2 or 3, it is Impossible to have a
valid configuration. If R = 2 or C = 2, valid configurations exists
only if M is even. For example, if R = 2, C = 7 and M = 5, it is
Impossible since M is odd. However, if M = 6, we can place the mines
on the left part of the board and click on the bottom right, like
this:
*....
*...c If the board is not in any of the above case, it means the board is at least 3 x 3 size. In this case, we can always
find a valid mine configuration if the number of empty cells is bigger
than 9. Here is one way to do it:
If the number of empty cells is equal or bigger than 3 * C, then the
mines can be placed row by row starting from top to bottom. If the
number of remaining mines can entirely fill the row or is less than C
- 2 then place the mines from left to right in that row. Otherwise, the number of remaining mines is exactly C - 1, place the last mine in
the next row. For example:
****** ******
*****. ****..
...... -> *.....
...... ......
.....c .....c If the number of empty cells is less than 3 * C but at least 9, we first fill all rows with mines except
the last 3 rows. For the last 3 rows, we fill the remaining mines
column by column from the left most column. If the remaining mines on
the last column is two, then last mine must be put in the next column.
For example:
****** ******
.... -> *...
**.... *.....
*....c *....c Now, we are left with at most 9 empty cells which are located in the 3 x 3 square cells at the bottom right
corner. In this case, we can check by hand that if the number of empty
cells is 5 or 7, it is Impossible to have a valid mine configuration.
Otherwise, we can hard-coded a valid configuration for each number of
empty cell in that 3 x 3 square cells.
Sigh... that was a lot of cases to cover! How do we convince ourselves
that when we code the solution, we do not miss any corner case?
Brute-force approach
For the small input, the board size is at most 5 x 5. We can check all
(25 choose M) possible mine configurations and find one that is valid
(i.e., clicking an empty cell in the configuration reveal all other
empty cells). To check whether a mine configuration is valid, we can
run a flood-fill algorithm (or a simple breath-first search) from the
clicked empty cell and verify that all other empty cells are reachable
(i.e., they are in one connected component). Note that we should also
check all possible click positions. This brute-force approach is fast
enough for the small input.
The brute-force approach can be used to check (for small values of R,
C, M) whether there is a false-negative in our enumeration strategy
above. A false-negative is found when there exist a valid mine
configuration, but the enumeration strategy above yields Impossible.
Once we are confident that our enumeration strategy does not produce
any false-negative, we can use it to solve the large input.
An easier to implement approach
After playing around with several valid mine configurations using the
enumeration strategy above, you may notice a pattern: in a valid mine
configuration, the number of mines in a particular row is always equal
or larger than the number of mines of the rows below it and all the
mines are left-aligned in a row. With this insight, we can implement a
simpler backtracking algorithm that places mines row by row from top
to bottom with non-increasing number of mines as we proceed to fill in
the next row and prune if the configuration for the current row is
invalid (it can be checked by clicking at the bottom right cell). This
backtracking with pruning can handle up to 50 x 50 sized board in
reasonable time and is simpler to implement (i.e., no need to
enumerate corner / tricky cases).
If the contest time were shorter, we may not have enough time to
enumerate all possible cases. In this case, betting on the
backtracking algorithm (or any other algorithm that is easier to
implement) may be a good idea. Finding such algorithms is an art :).

Related

A game relevant to maze

A and B play a game as follows:
For maze represented by matrix has size n × n, the cell contains character "." represents the movable land and the cell contains character "#" character represents an obstacle that cannot be stepped on. Starting from the cell (n, n), each person takes turns choosing a next position to move. The new position must be on the left or upward the current cell (Move as the rook in the chess). There are no obstacles in the middle of the current position and the new location. If you can't find a new position, that person will lose. A is the one who go first. Determine who is the winner.
For examples:
With n = 2 and the maze:
. .
. .
The result is B, because initially at coordinates (2,2), A will go left or upward so there will be coordinates (1,2) or (2,1). Then B will move to the coordinates (1,1). A can't move anymore so he loses and B wins.
With n = 5 and the maze:
. . # . .
# . . . #
. . # . .
# . . . .
. . . . .
Explaining similarly, we have A will be the winner.
This is my attempt: I tried to use recursive programming to determine who is the winner, but it takes too long when n is large, and I've tried to build dynamic-programming for this problem.
Edit:
So, the main of this problem to how we can determine who is the winner in this game. Suppose that initially at coordinates (n,n), we have a stone. A and B take turns playing a game as follows: A will choose a new position to move this stone (we can image that the stone like a rook in a chess), this new position must be on the left or above the current cell, after then B also choose a new position to move this stone. Until, the person who can't move this stone will be loser.
Note that: The char "." represents the movable land and the char "#" represents an obstacle!
And my purpose when post this problem is I want to try a dynamic-programming or recursion to determine who is the winner in this game.
Well you can just apply Sprague-Grundy theorem to find out the winner.
So calculating grundy numbers would be something like this:
Mark sure-loss cells with 0 (cells where we cant go up or left)
0 . # 0 .
# . . . #
0 . # . .
# . . . .
0 . . . .
Loop through remaining cells and for each unknown cell (marked .) find all reachable cells in one move
Now MEX of those cells will be value of unknown cell
After filling all cells we will have something like this:
0 1 # 0 1
# 0 1 2 #
0 2 # 1 0
# 3 0 4 1
0 4 1 3 2
So player will win if starting cell (n,n) is not 0
Sample code (C++), O(n^3):
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<string>A = {
"..#..",
"#...#",
"..#..",
"#....",
"....."};
int n = A.size();
int Grundy[n][n]={};
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
if(A[i][j]!='#')
{
int can[2*n+1]={};
int left = j-1;
while(left>=0 && A[i][left]!='#')
{
can[Grundy[i][left]]++;
left--;
}
int up = i-1;
while(up>=0 && A[up][j]!='#')
{
can[Grundy[up][j]]++;
up--;
}
while(can[Grundy[i][j]])Grundy[i][j]++;
}
cout<<(Grundy[n-1][n-1] ? "Player 1 wins\n" : "Player 2 wins\n");
}
This results in O(n^3) solution, though we can still optimize to O(n^2) as follows:
Because we only play one game board at a time, we don't really need all those grundy numbers, it's enough to know if there's grundy number 0 reachable from current cell
Let's call those cells with grundy value 0 losing cells, so now we can win from cell (i,j) if and only if we can move to some losing cell.
searching for reachable cells will still result in O(n^3) if we use for/while loop for this
To get O(n^2) we need to use prefix arrays for rows and columns.
so win[i][j] - stores if we can win from cell (i,j)
loseRow[i][j] - stores if we have any losing cells in row i that can be reached from cell (i,j)
loseCol[i][j] - stores if we have any losing cells in column j that can be reached from cell (i,j)
Sample code (C++), O(n^2):
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<string>A = {
"..#..",
"#...#",
"..#..",
"#....",
"....."};
int n = A.size();
int win[n][n]={};
int loseRow[n][n]={};
int loseCol[n][n]={};
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
if(A[i][j]!='#')
{
if(j-1>=0 && A[i][j-1]!='#')
{
win[i][j]|=loseRow[i][j-1];
loseRow[i][j]=loseRow[i][j-1];
}
if(i-1>=0 && A[i-1][j]!='#')
{
win[i][j]|=loseCol[i-1][j];
loseCol[i][j]=loseCol[i-1][j];
}
loseRow[i][j]|=!win[i][j];
loseCol[i][j]|=!win[i][j];
}
cout<<(win[n-1][n-1] ? "Player 1 wins\n" : "Player 2 wins\n");
}
We can classify coordinates in the matrix as winning (i.e. the side to move wins with correct play) or losing (i.e. the side to move loses with correct play).
A coordinate (r,c) corresponding to movable land is
losing if there are no legal moves;
losing if all legal moves go to winning coordinates;
winning if at least one legal move goes to losing coordinates.
Note that, according to the first rule, (1,1) is losing and therefore according to the last rule, whoever can move the stone to (1,1) wins.
The classification of your second matrix is:
L W # L W
# L W W #
L W # W L
# W L W W
L W W W W
Since the value of a coordinate depends only on values to the left and above, we can compute the values in top-to-bottom, left-to-right order. You don't really need recursion or dynamic programming. Something like
for r in 1...n
for c in 1...n
look left and up until the edge of the board or the first # sign
if there are any L values, then mark (r,c) as winning
otherwise mark (r,c) as losing
This naive approach takes O(n) time per coordinate, so O(n3) total time. This can be improved to O(n2) by maintaining some boolean flags as we scan the matrix:
One row flag indicating whether we can make a leftwards move from the current position to a losing position. A value of L will set the flag to true, while a # sign will reset it to false.
N column flags where fi indicates whether we can make an upwards move on column i to a losing position. Each flag changes values as described above.

Checking the validity of a pyramid of dominoes

I came across this question in a coding interview and couldn't figure out a good solution.
You are given 6 dominoes. A domino has 2 halves each with a number of spots. You are building a 3-level pyramid of dominoes. The bottom level has 3 dominoes, the middle level has 2, and the top has 1.
The arrangement is such that each level is positioned over the center of the level below it. Here is a visual:
[ 3 | 4 ]
[ 2 | 3 ] [ 4 | 5 ]
[ 1 | 2 ][ 3 | 4 ][ 5 | 6 ]
The pyramid must be set up such that the number of spots on each domino half should be the same as the number on the half beneath it. This doesn't apply to neighboring dominoes on the same level.
Is it possible to build a pyramid from 6 dominoes in the arrangement described above? Dominoes can be freely arranged and rotated.
Write a function that takes an array of 12 ints (such that arr[0], arr[1] are the first domino, arr[2], arr[3] are the second domino, etc.) and return "YES" or "NO" if it is possible or not to create a pyramid with the given 6 dominoes.
Thank you.
You can do better than brute-forcing. I don't have the time for a complete answer. So this is more like a hint.
Count the number of occurrences of each number. It should be at least 3 for at least two numbers and so on. If these conditions are not met, there is no solution. In the next steps, you need to consider the positioning of numbers on the tiles.
Just iterate every permutation and check each one. If you find a solution, then you can stop and return "YES". If you get through all permutations then return "NO". There are 6 positions and each domino has 2 rotations, so a total of 12*10*8*6*4*2 = 46080 permutations. Half of these are mirrors of each other so we're doubling our necessary workload, but I don't think that's going to trouble the user. I'd fix the domino orientations, then iterate through all the position permutations, then iterate the orientation permutations and repeat.
So I'd present the algorithm as:
For each permutation of domino orientations
For each permutation of domino positions
if arr[0] == arr[3] && arr[1] == arr[4] && arr[2] == arr[7] && arr[3] == arr[8] && arr[4] == arr[9] && && arr[5] == arr[10] then return "YES"
return "NO"
At that point I'd ask the interviewer where they wanted to go from there. We could look at optimisations, equivalences, implementations or move on to something else.
We can formulate a recursive solution:
valid_row:
if row_index < N - 1:
copy of row must exist two rows below
if row_index > 2:
matching left and right must exist
on the row above, around a center
of size N - 3, together forming
a valid_row
if row_index == N - 1:
additional matching below must
exist for the last number on each side
One way to solve it could be backtracking while tracking chosen dominoes along the path. Given the constraints on matching, a six domino pyramid ought to go pretty quick.
Before I start... There is an ambiguity in the question, which may be what the interviewer was more interested than the answer. This would appear to be a question asking for a method to validate one particular arrangement of the values, except for the bit which says "Is it possible to build a pyramid from 6 dominoes in the arrangement described above? Dominoes can be freely arranged and rotated." which implies that they might want you to also move the dominoes around to find a solution. I'm going to ignore that, and stick with the simple validation of whether it is a valid arrangement. (If it is required, I'd split the array into pairs, and then brute force the permutations of the possible arrangements against this code to find the first one that is valid.)
I've selected C# as a language for my solution, but I have intentionally avoided any language features which might make this more readable to a C# person, or perform faster, since the question is not language-specific, so I wanted this to be readable/convertible for people who prefer other languages. That's also the reason why I've used lots of named variables.
Basically check that each row is duplicated in the row below (offset by one), and stop when you reach the last row.
The algorithm drops out as soon as it finds a failure. This algorithm is extensible to larger pyramids; but does no validation of the size of the input array: it will work if the array is sensible.
using System;
public static void Main()
{
int[] values = new int[] { 3, 4, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6 };
bool result = IsDominoPyramidValid(values);
Console.WriteLine(result ? "YES" : "NO");
}
private const int DominoLength = 2;
public static bool IsDominoPyramidValid(int[] values)
{
int arrayLength = values.Length;
int offset = 0;
int currentRow = 1; // Note: I'm using a 1-based value here as it helps the maths
bool result = true;
while (result)
{
int currentRowLength = currentRow * DominoLength;
// Avoid checking final row: there is no row below it
if (offset + currentRowLength >= arrayLength)
{
break;
}
result = CheckValuesOnRowAgainstRowBelow(values, offset, currentRowLength);
offset += currentRowLength;
currentRow++;
}
return result;
}
private static bool CheckValuesOnRowAgainstRowBelow(int[] values, int startOfCurrentRow, int currentRowLength)
{
int startOfNextRow = startOfCurrentRow + currentRowLength;
int comparablePointOnNextRow = startOfNextRow + 1;
for (int i = 0; i < currentRowLength; i++)
{
if (values[startOfCurrentRow + i] != values[comparablePointOnNextRow + i])
{
return false;
}
}
return true;
}

How to efficiently detect a tie early in m,n,k-game (generalized tic-tac-toe)?

I'm implementing an m,n,k-game, a generalized version of tic-tac-toe, where m is the number of rows, n is the number of columns and k is the number of pieces that a player needs to put in a row to win. I have implemented a check for a win, but I haven't figured out a satisfactory way to check before the board is full of pieces, if no player can win the game. In other words, there might be empty slots on the board, but they cannot be filled in such a way that one player would win.
My question is, how to check this efficiently? The following algorithm is the best that I can think of. It checks for two conditions:
A. Go over all board positions in all 4 directions (top to bottom, right to left, and both diagonal directions). If say k = 5, and 4 (= k-1) consecutive empty slots are found, stop checking and report "no tie". This doesn't take into account for example the following situation:
OX----XO (Example 1)
where a) there are 4 empty consecutive slots (-) somewhere between two X's, b) next it is O's turn, c) there are less than four other empty positions on the board and no player can win by putting pieces to those, and d) it is not possible to win in any other direction than horizontally in the shown slots either. Now we know that it is a tie because O will eventually block the last winning possibility, but erroneously it is not reported yet because there are four consecutive empty slots. That would be ok (but not great). Checking this condition gives a good speed-up at the beginning when the checking algorithm usually finds such a case early, but it gets slower as more pieces are put on the board.
B. If this k-1-consecutive-empty-slots-condition isn't met, the algorithm would check the slots again consecutively in all 4 directions. Suppose we are currently checking from left to right. If at some point an X is encountered and it was preceded by an O or - (empty slot) or a board border, then start counting the number of consecutive X's and empty slots, counting in this first encountered X. If one can count to 5, then one knows it is possible for X to win, and "no tie" is reported. If an O preceded by an X is encountered before 5 consecutive X's, then X cannot win in those 5 slots from left to right starting from where we started counting. For example:
X-XXO (Example 2)
12345
Here we started checking at position 1, counted to 4, and encountered an O. In this case, one would continue from the encountered O in the same way, trying to find 5 consecutive O's or empty slots this time. In another case when counting X's or empty slots, an O preceded by one or more empty slots is encountered, before counting to 5. For example:
X-X-O (Example 3)
12345
In this case we would again continue from the O at position 5, but add to the new counter (of consecutive O's or empty slots) the number of consecutive empty slots that preceded O, here 1, so that we wouldn't miss for example this possible winning position:
X-X-O---X (Example 4)
In this way, in the worst case, one would have to go through all positions 4 times (4 directions, and of course diagonals whose length is less than k can be skipped), giving running time O(mn).
The best way I could think of was doing these two described checks, A and B, in one pass. If the checking algorithm gets through all positions in all directions without reporting "no tie", it reports a tie.
Knowing that you can check a win just by checking in the vicinity of the last piece that was added with running time O(k), I was wondering if there were quicker ways to do an early check for a tie. Doesn't have to be asymptotically quicker. I'm currently keeping the pieces in a two-dimensional array. Is there maybe a data structure that would allow an efficient check? One approach: what is the highest threshold of moves that one can wait the players to make before running any checks for a tie at all?
There are many related questions at Stack Overflow, for example this, but all discussions I could find either only pointed out the obvious tie condition, where the number of moves made is equal to the size of the board (or they checked if the board is full), or handled only the special case where the board is square: m = n. For example this answer claims to do the check for a tie in constant time, but only works when m = n = k. I'm interested in reporting the tie as early as possible and for general m,n and k. Also if the algorithm works for more than two players, that would be neat.
I would reduce the problem of determining a tie to the easier sub-problem:
Can player X still win?
If the answer is 'no' for all players, it is a tie.
To find out whether Player X can win:
fill all blank spaces with virtual 'X'-pieces
are there k 'X'-pieces in a row anywhere?
if there are not --> Player X cannot win. return false.
if there are, find the row of k stones with the least amount of virtual pieces. Count the number of virtual pieces in it.
count the number of moves player X has left, alternating with all other players, until the board is completely full.
if the number of moves is less than the amount of virtual pieces required to win, player X cannot win. return false.
otherwise, player X can still win. return true.
(This algorithm will report a possible win for player X even in cases where the only winning moves for X would have another player win first, but that is ok, since that would not be a tie either)
If, as you said, you can check a win just by checking in the vicinity of the last piece that was added with running time O(k), then I think you can run the above algorithm in O(k * Number_of_empty_spots): Add all virtual X-Piece, note any winning combinations in the vicinity of the added pieces.
The number of empty slots can be large, but as long as there is at least one empty row of size k and player X has still k moves left until the board is filled, you can be sure that player X can still win, so you do not need to run the full check.
This should work with any number of players.
Actually the constant time solution you referenced only works when k = m = n as well. If k is smaller then I don't see any way to adapt the solution to get constant time, basically because there are multiple locations on each row/column/diagonal where a winning consecutive k 0's or 1's may occur.
However, maintaining auxiliary information for each row/column/diagonal can give a speed up. For each row/column/diagonal, you can store the start and end locations for consecutive occurrences of 1's and blanks as possible winning positions for player 1, and similarly store start and end locations of consecutive occurrences of 0's and blanks as possible winning positions for player 0. Note that for a given row/column/diagonal, intervals for player 0 and 1 may overlap if they contain blanks. For each row/column/diagonal, store the intervals for player 1 in sorted order in a self-balancing binary tree (Note you can do this because the intervals are disjoint). Similarly store the intervals for player 0 sorted in a tree. When a player makes a move, find the row/column/diagonals that contain the move location and update the intervals containing the move in the appropriate row column and diagonal trees for the player that did not make the move. For the player that did not make a move, this will split an interval (if it exists) into smaller intervals that you can replace the old interval with and then rebalance the tree. If an interval ever gets to length less than k you can delete it. If a tree ever becomes empty then it is impossible for that player to win in that row/column/diagonal. You can maintain a counter of how many rows/columns/diagonals are impossible to win for each player, and if the counter ever reaches the total number of rows/columns/diagonals for both players then you know you have a tie. The total running time for this is O(log(n/k) + log(m/k)) to check for a tie per move, with O(mn/k) extra space.
You can similarly maintain trees that store consecutive intervals of 1's (without spaces) and update the trees in O(log n + log m) time when a move is made, basically searching for the positions before and after the move in your tree and updating the interval(s) found and merging two intervals if two intervals (before and after) are found. Then you report a win if an interval is ever created/updated and obtains length greater than or equal to k. Similarly for player 0. Total time to check for a win is O(log n + log m) which may be better than O(k) depending on how large k is. Extra space is O(mn).
Let's look at one row (or column or diagonal, it doesn't matter) and count the number of winning lines of length k ("k-line") it's possible to make, at each place in the row, for player X. This solution will keep track of that number over the course of the game, checking fulfillment of the winning condition on each move as well as detecting a tie.
1 2 3... k k k k... 3 2 1
There is one k-line including an X in the leftmost slot, two with the second slot from the left, and so on. If an opposing player, O or otherwise, plays in this row, we can reduce the k-line possibility counts for player X in O(k) time at the time of the move. (The logic for this step should be straightforward after doing an example, needing no other data structure, but any method involving checking each of the k rows of k from will do. Going left to right, only k operations on the counts is needed.) An enemy piece should set the possibility count to -1.
Then, a detectably tied game is one where no cell has a non-zero k-line possibility count for any player. It's easy to check this by keeping track of the index of the first non-zero cell. Maintaining the structure amounts to O(k*players) work on each move. The number of empty slots is less than those filled, for positions that might be tied, so the other answers are good for checking a position in isolation. However, at least for reasonably small numbers of players, this problem is intimately linked with checking the winning condition in the first place, which at minimum you must do, O(k), on every move. Depending on your game engine there may be a better structure that is rich enough to find good moves as well as detect ties. But the possibility counting structure has the nice property that you can check for a win whilst updating it.
If space isn't an issue, I had this idea:
For each player maintain a structure sized (2mn + (1 - k)(m + n) + 2(m - k + 1)(n - k + 1) + 2(sum 1 to (m - k))) where each value represents if one of another player's moves are in one distinct k-sized interval. For example for a 8-8-4 game, one element in the structure could represent row 1, cell 0 to 3; another row 1, cell 1 to 4; etc.
In addition, one variable per player will represent how many elements in their structure are still unset. Only one move is required to set an element, showing that that k-interval can no longer be used to win.
An update of between O(k) and O(4k) time per player seems needed per move. A tie is detected when the number of players exceeds the number of different elements unset.
Using bitsets, the number of bytes needed for each player's structure would be the structure size divided by 8. Notice that when k=m=n, the structure size is 4*k and update time O(4). Less than half a megabyte per player would be needed for a 1000,1000,5 game.
Below is a JavaScript example.
var m = 1000, n = 1000, k = 5, numberOfPlayers = 2
, numberOfHorizontalKIs = m * Math.max(n - k + 1,0)
, numberOfverticalKIs = n * Math.max(m - k + 1,0)
, horizontalVerticalKIArraySize = Math.ceil((numberOfHorizontalKIs + numberOfverticalKIs)/31)
, horizontalAndVerticalKIs = Array(horizontalVerticalKIArraySize)
, numberOfUnsetKIs = horizontalAndVerticalKIs
, upToM = Math.max(0,m - k) // southwest diagonals up to position m
, upToMSum = upToM * (upToM + 1) / 2
, numberOfSouthwestKIs = 2 * upToMSum //sum is multiplied by 2 to account for bottom-right-corner diagonals
+ Math.max(0,n - m + 1) * (m - k + 1)
, diagonalKIArraySize = Math.ceil(2 * numberOfSouthwestKIs/31)
, diagonalKIs = Array(diagonalKIArraySize)
, numberOfUnsetKIs = 2 * numberOfSouthwestKIs + numberOfHorizontalKIs + numberOfverticalKIs
function checkTie(move){
var row = move[0], column = move[1]
//horizontal and vertical
for (var rotate=0; rotate<2; rotate++){
var offset = Math.max(k - n + column, 0)
column -= offset
var index = rotate * numberOfHorizontalKIs + (n - k + 1) * row + column
, count = 0
while (column >= 0 && count < k - offset){
var KIArrayIndex = Math.floor(index / 31)
, bitToSet = 1 << index % 31
if (!(horizontalAndVerticalKIs[KIArrayIndex] & bitToSet)){
horizontalAndVerticalKIs[KIArrayIndex] |= bitToSet
numberOfUnsetKIs--
}
index--
column--
count++
}
//rotate board to log vertical KIs
var mTmp = m
m = n
n = mTmp
row = move[1]
column = move[0]
count = 0
}
//rotate board back
mTmp = m
m = n
n = mTmp
// diagonals
for (var rotate=0; rotate<2; rotate++){
var diagonalTopColumn = column + row
if (diagonalTopColumn < k - 1 || diagonalTopColumn >= n + m - k){
continue
} else {
var offset = Math.max(k - m + row, 0)
row -= offset
column += offset
var dBeforeM = Math.min (diagonalTopColumn - k + 1,m - k)
, dAfterM = n + m - k - diagonalTopColumn
, index = dBeforeM * (dBeforeM + 1) / 2
+ (m - k + 1) * Math.max (Math.min(diagonalTopColumn,n) - m + 1,0)
+ (diagonalTopColumn < n ? 0 : upToMSum - dAfterM * (dAfterM + 1) / 2)
+ (diagonalTopColumn < n ? row : n - 1 - column)
+ rotate * numberOfSouthwestKIs
, count = 0
while (row >= 0 && column < n && count < k - offset){
var KIArrayIndex = Math.floor(index / 31)
, bitToSet = 1 << index % 31
if (!(diagonalKIs[KIArrayIndex] & bitToSet)){
diagonalKIs[KIArrayIndex] |= bitToSet
numberOfUnsetKIs--
}
index--
row--
column++
count++
}
}
//mirror board
column = n - 1 - column
}
if (numberOfUnsetKIs < 1){
return "This player cannot win."
} else {
return "No tie."
}
}

Lego Blocks - Dynamic Programming

I'm trying to solve following DP problem:
You have 4 types of lego blocks, of sizes 1 * 1 * 1, 1 * 1 * 2, 1 * 1
* 3 and 1 * 1 * 4. Assume you have infinite number of blocks of each type.
You want to make a wall of height H and width M out of these blocks.
The wall should not have any holes in it. The wall you build should be
one solid structure. A solid structure means that it should not be
possible to separate the wall along any vertical line without cutting
any lego block used to build the wall. The blocks can only be placed
horizontally. In how many ways can the wall be built?
Here is how I'm attempting it:
Representing 1 * 1 * 1, 1 * 1 * 2, 1 * 1 * 3 and 1 * 1 * 4 blocks with a b c d
. Valid patterns are indicated in bold. Invalid patterns are which can be broken by vertical line.
H=1 & W=3 #valid pattern=1 aa ab ba c
H=2 & W=3 #valid pattern=9
I'm trying to find recurrence pattern either to extend this by height or width.i.e to find values for H=3 & W=3 or H=2&W=4.
Any inputs on how to formula-ize growth for this by height or weight?
P.S. The wall be always H*W*1.
First, let's see how many M*N walls can we build if we neglect the need to keep them connected:
We can treat each row separately, and then multiply the counts since they are independent.
There is only one way to tile a 0*1 or a 1*1 wall, and the number of ways to tile an n*1 is the total of the number of ways to tile {n-1}*1...{n-4}*1-sized walls, the reason being these walls can be obtained by removing the last tile of the n*1 wall.
This gives rise to a tetranacci sequence, OEIS A000078.
The number of all W*H walls is a(w,h)=T(w)^h.
Now, to count the number of solid walls. MBo's answer already contains the basic premise:
Branch on the leftmost place where the wall is not connected. The number of All W*H walls is the number of Solid X*H walls times the number of All {W-X}*H walls, summed across all possible values of X, plus the number of Solid W*H walls:
A(W,H) = sum{X=1..{W-1}}(S(X,H)*A(W-X,H)) + S(W,H)
As a last step, we separate S(M,H) term, which is the value we want to calculate, and repeat the previous formulas:
S(W,H) = A(W,H) - sum_x( S(X,H)*A(W-X,H) ) //implicitly, S(1,H)=1
A(W,H) = T(W)^H
T(X) = X > 0: T(X-1)+T(X-2)+T(X-3)+T(X-4)
X = 0: 1
X < 0: 0
(proving MBo's formula correct).
This also provides an O(W^2) algorithm to compute S (assuming proper memoization and constant-time arithmetic operations)
It is not hard to find a number of 1xW stripes (let it is N(1,W)).
Then you can find a number of all (including non-solid) HxW walls - it is A(H,W) = N(1,W)^H
Any non-solid wall consists of left H*L wall and right H*(W-L) wall. It seems that number of solid walls is
S(H,W) = A(H,W) - Sum(S(H, L) * A(H, W-L)) [L=1..W-1]
S(H, L) * A(H, W-L) is number of non-solid walls with the leftmost break at L vertical position. First factor is the number of solid walls - to eliminate counting of repetitive variants.
My Python 3 implementation
def tetranacci(n):
arr = [1, 2, 4, 8]
if n <= 4:
return arr[:n]
else:
for i in range(4, n):
arr.append(sum(arr[i-4:i])%(10**9 + 7))
return arr
def legoBlocks(n, m):
MOD = (10**9 +7)
a, s = [(v**n)%MOD for v in tetranacci(m)], [1]
for i in range(1, len(a)):
sums = sum([x*y for x,y in zip(a[:i], s[::-1])])
s.append( (a[i]-sums)%MOD)
return s[-1]

Calculating number of moves from top left corner to bottom right with move in any direction

I have a problem asked to me in an interview, this is a similar problem I found so I thought of asking here. The problem is
There is a robot situated at (1,1) in a N X N grid, the robot can move in any direction left, right ,up and down. Also I have been given an integer k, which denotes the maximum steps in the path. I had to calculate the number of possible ways to move from (1,1) to (N,N) in k or less steps.
I know how to solve simplified version of this problem, the one with moves possible in only right and down direction. That can be solved with Dynamic Programming. I tried applying the same technique here but I don't think it could be solved using 2-dimensional matrix, I tried a similar approach counting possible number of ways from left or up or right and summing up in down direction, but the problem is I don't know number of ways from down direction which should also be added. So I go in a loop. I was able to solve this problem using recursion, I could recurse on (N,N,k) call for up, left and k-1, sum them up but I think this is also not correct, and if it could be correct it has exponential complexity. I found problems similar to this so I wanted to know what would be a perfect approach for solving these types of problems.
Suppose you have an NxN matrix, where each cell gives you the number of ways to move from (1,1) to (i,j) in exactly k steps (some entries will be zero). You can now create an NxN matrix, where each cell gives you the number of ways to move from (1,1) to (i,j) in exactly k+1 steps - start off with the all-zero matrix, and then add in cell (i,j) of the previous matrix to cells (i+1, j), (i, j+1),... and so on.
The (N,N) entry in each of the k matrices gives you the number of ways to move from (1,1) to (i,j) in exactly k steps - all you have to do now is add them all together.
Here is an example for the 2x2 case, where steps outside the
matrix are not allowed, and (1,1) is at the top left.
In 0 steps, you can only get to the (1,1) cell:
1 0
0 0
There is one path to 1,1. From here you can go down or right,
so there are two different paths of length 1:
0 1
1 0
From the top right path you can go left or down, and from the
bottom left you can go right or up, so both cells have paths
that can be extended in two ways, and end up in the same two
cells. We add two copies of the following, one from each non-zero
cell
1 0
0 1
giving us these totals for paths of length two:
2 0
0 2
There are two choices from each of the non-empty cells again
so we have much the same as before for paths of length three.
0 4
4 0
Two features of this are easy checks:
1) For each length of path, only two cells are non-zero,
corresponding to the length of the path being odd or even.
2) The number of paths at each stage is a power of two, because
each path corresponds to a choice at each step as to whether to
go horizontally or vertically. (This only holds for this simple
2x2 case).
Update: This algorithm is incorrect. See the comments and mcdowella's answer. However, the corrected algorithm does not make a difference to the time complexity.
It can be done in O(k * N^2) time, at least. Pseudocode:
# grid[i,j] contains the number of ways we can get to i,j in at most n steps,
# where n is initially 0
grid := N by N array of 0s
grid[1,1] := 1
for n from 1 to k:
old := grid
for each cell i,j in grid:
# cells outside the grid considered 0 here
grid[i,j] := old[i,j] + old[i-1,j] + old[i+1,j] + old[i,j-1] + old[i,j+1]
return grid[N,N]
There might be an O(log k * (N*log N)^2) solution which is way more complex. Each iteration through the outer for loop is nothing but a convolution with a fixed kernel. So we can convolve the kernel with itself to get bigger kernels that fuse multiple iterations into one, and use FFT to compute the convolution.
Basically uniquepaths( row, column ) = 0 if row > N || column > N
1 if row ==N && column == N
uniquepaths(row+1, column) + uniquePaths(row, column+1)
i.e, the solution have optimal substructure and overlapped subproblems. So, it can be solved using Dynamic Programming. Below is memorization (lazy/on demand) version of it (related which basically returns paths as well: Algorithm for finding all paths in a NxN grid) (you may refer to my blog for more details: http://codingworkout.blogspot.com/2014/08/robot-in-grid-unique-paths.html)
private int GetUniquePaths_DP_Memoization_Lazy(int?[][] DP_Memoization_Lazy_Cache, int row,
int column)
{
int N = DP_Memoization_Lazy_Cache.Length - 1;
if (row > N)
{
return 0;
}
if (column > N)
{
return 0;
}
if(DP_Memoization_Lazy_Cache[row][column] != null)
{
return DP_Memoization_Lazy_Cache[row][column].Value;
}
if((row == N) && (column == N))
{
DP_Memoization_Lazy_Cache[N][N] = 1;
return 1;
}
int pathsWhenMovedDown = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row + 1, column);
int pathsWhenMovedRight = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row, column + 1);
DP_Memoization_Lazy_Cache[row][column] = pathsWhenMovedDown + pathsWhenMovedRight;
return DP_Memoization_Lazy_Cache[row][column].Value;
}
where the caller is
int GetUniquePaths_DP_Memoization_Lazy(int N)
{
int?[][] DP_Memoization_Lazy_Cache = new int?[N + 1][];
for(int i =0;i<=N;i++)
{
DP_Memoization_Lazy_Cache[i] = new int?[N + 1];
for(int j=0;j<=N;j++)
{
DP_Memoization_Lazy_Cache[i][j] = null;
}
}
this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache, row: 1, column: 1);
return DP_Memoization_Lazy_Cache[1][1].Value;
}
Unit Tests
[TestCategory(Constants.DynamicProgramming)]
public void RobotInGridTests()
{
int p = this.GetNumberOfUniquePaths(3);
Assert.AreEqual(p, 6);
int p1 = this.GetUniquePaths_DP_Memoization_Lazy(3);
Assert.AreEqual(p, p1);
var p2 = this.GetUniquePaths(3);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
p = this.GetNumberOfUniquePaths(4);
Assert.AreEqual(p, 20);
p1 = this.GetUniquePaths_DP_Memoization_Lazy(4);
Assert.AreEqual(p, p1);
p2 = this.GetUniquePaths(4);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
}
There will be infinite no of ways. This is because you can form an infinite loop of positions and thus infinite possibilities. For ex:- You can move from (0,0) to (0,1) then to (1,1), then (1,0) and back again to (0,0). This forms a loop of positions and thus anyone can go round and round these types of loops and have infinite possibilities.

Resources