Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Avoidland is a puzzle played on an n×n board with n pawns. The pawns are initially placed on the squares of the board, at most one pawn per square. The goal is to move the pawns so that they “avoid” each other—there cannot be a row or a column with more than one pawn. In one move a pawn can move to a neighboring unoccupied square, that is, a square that shares a side with the pawn’s current location and there is no pawn on it. Given the initial locations of the pawns, what is the minimum number of moves needed to solve the puzzle?
Input
The first line contains an integer n, then n lines follow. The i-th line contains the initial row and column coordinates of the i-th pawn, separated by space. Each coordinate is an integer between 1 and n. You may assume that n is at most 1000000.
Output
The line contains the minimum number of moves needed to solve the puzzle.
Sample Input 1
3
1 3
2 3
3 1
Sample Output 1
1
Sample Input 2
4
1 4
4 1
1 1
4 4
Sample Output 2
4
My approach:
The solution would require each row and column to have 1 pawn exactly.
For the initial configuration, make a rightmost column which contains the sum of the number of pawns in each row.
Make a bottom row which contains the sum of the number of pawns in each column.
Now we need to find the minimum number of steps to make each of these arrays into all 1s and add them up but I am confused how to do that.
I think your approach is a great one. Once you have computed the histogram of pawns in each row/column, you can use a greedy algorithm to count the moves.
Suppose we have a 0,0,3,1 histogram that we need to change into 1,1,1,1.
It is clear that we may as well move the pawn closest to the left into the first position, the second pawn closest to the left into the second position, etc.
Therefore simply iterate through the histogram and add up the distance between the i^th pawn found and position i (where we want to place it).
e.g. in Python:
A = [0,0,3,1]
t = 0
i = 0
for pos,count in enumerate(A):
for k in range(count):
t += abs(pos-i)
i += 1
print t
This prints an answer of 3, corresponding to moving one pawn left twice, and one pawn left once.
The complexity is O(n) because the inner loop is executed exactly once for each pawn.
The guess with histogram is cool. That is the starting point. Let's note that changing for instance horizontal histogram doen't have effect on vertical histogram and the opposite is also true. So you should apply your algorithm twice on vertical and horizontal histogram, but algorithms are the same. So I will consider only horizontal one.
Now suppose we have the following {U(0), U(1), ..., U(n-2), O(n-1)} here O(k) represents occupied and U(k) unoccupied square on k-th position. Now it is obvious that moving a pawn from j position to i position when all squares are unoccupied is just the diffrence of their positions j-i. Now we will show that it is also true for general case when we have some occupied squares. Suppose we have the following histogram:
{..U(i),..O(k1),..,O(k2),..,O(kl),..,O(j),...}
I.e. we want to move O(j) to U(i) but we also have l squares occupied. Lets first move O(k1) to U(i): that is done in k1-i steps. Now we move O(k2) to U(k1): k2-k1 steps.....And in the end move O(j) to U(kl): j-kl steps. What we have:
(k1-i)+(k2-k1)+(k3-k2)+....+(kl-k(l-1))+(j-kl) => j-i
So we proved that moving a pawn from one position to nother is just the diffrence in positions.
Now the implementation in C#. We will do 2 parallel walk through the histogram. To understand it lets consider example:
0 0 0 3 2
i will search for empty position, j will search for position where count > 1.
int i = 0, j = 0, c = 0;
while(i < n && j < n)
{
while(h[i] <> 0) i++;
while(h[j] < 2) j++;
c += Math.Abs(j - i++);
h[j] = h[j] - 1;
}
Firt step: i == 0, j == 3, c == 3, h[3] == 2
Second: i == 1, j == 3, c == 5, h[3] == 1
Third: i == 2, j == 4, c == 7, h[4] == 1
Complexity of this algorithm is O(2n). We have 2 histograms so whole complexity is O(4n) = O(n).
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 :).
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."
}
}
There's a Fibonacci code definied like that:
C(0) = 0
C(1) = 1
C(2) = 01
C(3) = 101
C(4) = 01101
C(5) = 10101101
.
.
.
There's a game using this coding system and a there's a board (eg. 1101101). In this game, players are removing Fibonecci's codes by turns (from the right to the left) out of the board. Player loses, if he can't remove anything anymore (the board becomes empty and now it's player's turn).
Is there a way to make an algorithm, that decides whether a player (that is doing 1st move) can always (always means independently from another player's movemenets) win or not.
egxample:
Board: 1101101
Player1 - 1st move (01) // possible movements (01, 1, 101, 01101)
Board: 11011
Player2 - 2nd move (1) // possible movements (1)
Board: 1101
Player1 - 3rd move (01) // possible movements (1, 01, 101)
Board: 11
Player2 - 4th move (1) // possible movements (1)
Board: 1
Player1 - 5th move (1) // possible movements (1)
Board:
Player2 - 6th move (-) // possible movements (none)
Let n be the length of the initial board, and let board[i] be the i-th symbol (from the left, 1-based indices) in the initial board, and board[i..j] the segment of the initial board between the indices i and j inclusive.
Let us call i a winning length if the player whose turn it is when board[1..i] is left can force a win, and a losing length if each possible move from board[1..i] leaves a winning length for the other player.
0 is a losing length (no possible move at all), and 1 is a winning length (removing the only remaining symbol immediately wins).
Each length is either winning or losing: if any possible move leaves a losing length for the other player, we can force a win by choosing that move, otherwise it's a losing length by definition.
We can find out whether we can force a win, together with a winning strategy if that is the case by noting for each length if it is losing (mark as -1) or what a win-forcing move would be (a non-negative integer k such that removing C[k] from the end of the remaining board leaves a losing length for the opponent; such a k is generally not unique, any will do).
Pseudocode (C-ish):
int moves[n+1];
// initialise all lengths to losing
for(i = 0; i <= n; ++i) {
moves[i] = -1;
}
moves[1] = board[i]; // win by removing the last symbol
for(i = 2; i <= n; ++i) {
if (board[i] == 0) {
// no choice, the only code ending with a 0 is C[0]
moves[i] = moves[i-1] < 0 ? 0 : -1;
} else {
k = 1;
while(C[k] is_suffix_of board[1..i]) {
if (moves[i - fib(k)] < 0) {
// found a winning move
moves[i] = k;
break;
}
++k;
}
// we either found a winning move and noted it, or leave the position as losing
}
}
moves[n] tells us whether the first player can force a win, and if so, how.
The advantage over a pure brute-force is that it saves a lot of recomputation, since all states but the first few are reachable in many ways.
The C[k] is_suffix_of board[1..i] check is the most costly operation, it can be somewhat improved by noting that C[k+1] is the concatenation of C[k-1] and C[k] in that order, so when you know that C[k] is a suffix of board[1..i], you only need to check whether C[k-1] is a suffix of board[1..(i-fib(k))].
Looking at the length of your series, i would say just brute-force it....
Should not be to hard to write and keep track of all moves.
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.