Sort polygon's points for drawing - algorithm

I have a matrix (0 means nothing, 1 means terrain) that represents a level in my game. The matrix corresponds to a grid that my screen is broken up into, and indicates where my terrain goes.
My terrain is actually composed of 4 points in the corners of each block within the grid. When you have multiple blocks that are connected, I use a merge-cell algorithm that removes the duplicate points and any interior points. The result is that I end up with a list of points representing only the outer edges of the polygon.
To draw this polygon, I need the points to be in some sort of order (either clockwise or counter-clockwise) such that each point is followed by it's neighboring point. Obviously the first and last points need to be neighbors. Since this is all in a grid, I know the exact distance between neighboring points.
The problem is that I am having trouble coming up with an algorithm that allows me to "walk" around the edge of the polygon while putting the points in order. I believe there should be a way to utilize the fact that I have the matrix representing the geometry, meaning there is only 1 possible way to draw the polygon (even if it is concave).
I have tried several approaches using greedy-type algorithms, but can't seem to find a way to know, in every case, which direction I want to travel in. Given that any particular point can have up to 3 neighbors (the fourth isn't included because it is the "starting" point, meaning that I have already sorted it) I need a way of knowing which way to move.
Update
Another approach that I have been trying is to sort the points by their X (with tiebreaker of Y) which gives me the topmost/leftmost edge. It also guarantees that I am starting on an outer edge. However, I'm still struggling to find an algorithm that guarantees that I stay on the outside without crossing over.
Here is an example matrix:
0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 1 1 1 0 0
Which corresponds to this (black dots represent my points):

First of all please consider that for a general matrix the output can be composed of more than one closed loop; for example boundaries of the matrix
form three distinct loops, one of them placed inside another.
To extract these loops the first step is to build a map of all "walls": you have a vertical wall each time the content of one cell is different from the next cell on the same row; you have instead an horizontal wall when the content is different from the same cell in the next row.
data = [[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 ],
[ 0, 1, 0, 0, 1, 0, 1, 1, 0, 0 ],
[ 0, 1, 0, 0, 1, 0, 1, 1, 1, 0 ],
[ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]]
rows = len(data)
cols = len(data[0])
walls = [[2*(data[r][c] != data[r][c+1]) + (data[r][c] != data[r+1][c])
for c in range(cols-1)]
for r in range(rows-1)]
In the example above I'm using two bits: 0x01 to mark horizontal walls and 0x02 to mark vertical walls. For a given (r, c) cell the walls are the right and bottom wall of the cell.
For simplicity I'm also assuming that the interesting areas are not touching the limits of the matrix; this can be solved by either adding extra rows and cols of zeros or by wrapping matrix access in a function that returns 0 for out-of-matrix virtual elements.
To build the list of boundaries you need to simply start from any point on a wall and move following walls, removing the walls from the map as you process them. When you cannot move any more a cycle has been completed (you're guaranteed to complete cycles because in a graph built in this way from a matrix of inside/outside flags the degree is guaranteed to be even in all vertices).
Filling all those cycles simultaneously using odd-even filling rules is also guaranteed to reproduce the original matrix.
In the code following I'm using r and c as row/col index and i and j instead to represent points on the boundary... for example for cell (r=3, c=2) the schema is:
where the red wall corresponds to bit 0x02 and the green wall to bit 0x01. The walls matrix has one row and one column less than the original data matrix because it's assumed that no walls can be present on last row or column.
result = []
for r in range(rows-1):
for c in range(cols-1):
if walls[r][c] & 1:
i, j = r+1, c
cycle = [(i, j)]
while True:
if i < rows-1 and walls[i][j-1] & 2:
ii, jj = i+1, j
walls[i][j-1] -= 2
elif i > 0 and walls[i-1][j-1] & 2:
ii, jj = i-1, j
walls[i-1][j-1] -= 2
elif j < cols-1 and walls[i-1][j] & 1:
ii, jj = i, j+1
walls[i-1][j] -= 1
elif j > 0 and walls[i-1][j-1] & 1:
ii, jj = i, j-1
walls[i-1][j-1] -= 1
else:
break
i, j = ii, jj
cycle.append((ii, jj))
result.append(cycle)
Basically the code starts from a point on a boundary and the checks if it can move on a wall going up, down, left or right. When it cannot move any more a cycle has been completed and can be added to the final result.
The complexity of the algorithm is O(rows*cols), i.e. it's proportional to the input size and it's optimal (in big-O sense) because you cannot compute the result without at least reading the input. This is easy to see because the body of the while cannot be entered more times than the total number of walls in the map (at each iteration a wall is removed).
Edit
The algorithm can be modified to generate as output only simple cycles (i.e. paths in which each vertex is visited only once).
result = []
index = [[-1] * cols for x in range(rows)]
for r in range(rows-1):
for c in range(cols-1):
if walls[r][c] & 1:
i, j = r+1, c
cycle = [(i, j)]
index[i][j] = 0
while True:
if i > 0 and walls[i-1][j-1] & 2:
ii, jj = i-1, j
walls[i-1][j-1] -= 2
elif j > 0 and walls[i-1][j-1] & 1:
ii, jj = i, j-1
walls[i-1][j-1] -= 1
elif i < rows-1 and walls[i][j-1] & 2:
ii, jj = i+1, j
walls[i][j-1] -= 2
elif j < cols-1 and walls[i-1][j] & 1:
ii, jj = i, j+1
walls[i-1][j] -= 1
else:
break
i, j = ii, jj
cycle.append((ii, jj))
ix = index[i][j]
if ix >= 0:
# closed a loop
result.append(cycle[ix:])
for i_, j_ in cycle[ix:]:
index[i_][j_] = -1
cycle = cycle[:ix+1]
index[i][j] = len(cycle)-1
This is implemented by adding to the output a separate cycle once the same vertex is met twice in the processing (the index table stores for a given i,j point the 0-based index in the current cycle being built).

This seems like it would work to me:
For every filled square, check which of its neighbours are filled. For those that aren't, add the appropriate edges to a list of edges. Generate those edges as directed, either clockwise or anticlockwise as you prefer.
To construct a full path, start by pulling any edge from the set and add it to the path. It has an order so look at the second vertex. Find the edge in the set with the first vertex that is equal to that second vertex. Pull that edge from the set and add it to the path. Continue until the path is closed.
Repeat to generate a list of paths. A simple polygon should end up as one path. A complex polygon — one with holes in the middle in this case — will be several.

I guess there are different ways to do this, I suppose there is quite simple one for case when diagonal connected cells counted as different contours:
You just need too keep cell and corner direction. For example you started from upper right corner of some earth cell (it supposed that either upper or right cell, or both are nothing if it is bourder) and want to go clockwise.
If cell to the right is earth, than you change current cell to it and change corner to upper left (it is the same point). Then you go to next iteration.
In other case, if you started from upper right corner of some earth cell and want to go clockwise. If cell to the right is NOT earth than you don't change current cell and change corner to bottom right, (it's next point)
So you also have symmetrical situation for other three possible corners, and you can go to next iteration until returning to start point.
So here is pseudo-code I wrote, it uses the same indexing as picture uses, and supposes that all cells along borders are free, otherwise you will need to check if index id not out of range.
I will also need additional array with almost the same dimensions as matrix to mark processed contours, it need to be 1 cell wider than matrix cause I'm going to mark vertical lines, and each vertical line is supposed to have coordinates of cell to the right of it. Note that there are only 2 cases midst 8 dwscribed above when you need to mark vertical line.
int mark[,] = new int[height,width+1]
start_i = i = 0;
start_j = j = 0;
direction = start_direction = top_left;
index = 0;
//outer cycle through different contours
while(true)
{
++index;
//scanning for contours through all the matrix
//continue from the same place, we stopped last time
for(/*i = i*/; i < n; i++)
{
for(/*j = j*/; j < n; j++)
{
//if we found earth
if(m[i,j] == 1)
{
//check if previous cell is nothing
//check if line between this and previous contour doesn't already added
if(m[i,j - 1] == 0 && mark[i,j] == 0)
{
direction = bottom_left;
break;
}
//the same for next cell
if(m[i,j + 1] == 0 && mark[i,j+1] == 0)
{
direction = top_right;
break;
}
}
}
//break if we found contour
if(i != start_i || j != start_j)
break;
}
//stop if we didn't find any contour
if(i == start_i && j == start_j)
{
break;
}
polygon = new polygon;
start_i = i;
start_j = j;
start_direction = direction;
//now the main part of algorithm described above
do
{
if(direction == top_left)
{
if(n(i-1,j) == 1)
{
direction = bottom_left;
position = (i-1,j)
}
else
{
direction = top_right;
polygon.Add(i,j+1);
}
}
if(direction == top_right;)
{
if(n[i,j + 1] == 1)
{
direction = top_left;
position = (i,j + 1)
}
else
{
direction = bottom_right;
mark[i, j + 1] = index;//don't forget to mark edges!
polygon.Add(i+1,j+1);
}
}
if(direction == bottom_right;
{
if(n[i+1,j] == 1)
{
direction = top_right;
position = (i+1,j)
}
else
{
direction = bottom_left;
polygon.Add(i+1,j);
}
}
if(direction == bottom_left)
{
if(n[i,j - 1] == 1)
{
direction = bottom_right;
position = [i,j - 1]
}
else
{
direction = top_left;
mark[i, j] = index;//don't forget to mark edges!
polygon.Add(i,j);
}
}
//and we can stop as we reached the starting state
}while(i != start_i || j != start_j || direction != start_direction);
//stop if it was last cell
if(i == n-1 && j == n- 1)
{
break;
}
}
Also you may need to know which contour is inside which, and you mat need a stack to keep what contours you are inside while you are scanning, so every time you are crossing the existing contour you need to add it to the stack or remove if it is already at the top of the stack.
It will cause the next changes in code:
...
//continue from the same place, we stopped last time
for(/*i = i*/; i < n; i++)
{
for(/*j = j*/; j < n; j++)
{
if(mark[i,j] != 0)
{
if(stack.top() == mark [i,j])
{
stack.pop();
}
else
{
stack.push(mark [i,j]);
}
}
//if we found earth
if(m[i,j] == 1)
{
...

If your matrix can contain random patterns, the answer is far more complicated than it seems.
For one thing they may be an arbitrary number of distinct polygons, and each of them might be hollow.
Besides, finding the contour of a region (even with no holes) is of little help for drawing the surface. Your GPU will eventually need triangles, which means you will need to decompose your polygons into rectangles.
Finding an optimal decomposition of a hollow bunch of squares (i.e. the smallest set of rectangles that will cover them all) is a well studied NP-complete problem with no known solution.
There exist algorithms to find an optimal decomposition of such shapes with no holes, but they are very complex.
A greedy algorithm is much easier to implement and usually yields acceptable results.
So I would do a greedy search on your matrix, collecting rectangles until all "1" values have been visited. Turning these rectangles into coordinates should be easy enough, since you know exactly where the top left and bottom right corners are.
The greedy scan will look like:
while your matrix is not empty
move to first "1" value. This is the rectangle top left corner
from this corner, extend the rectangle in x and y to maximize its surface
store the rectangle in a list and clear all corresponding "1" values

Related

Fastest way to find path from top left to bottom right of a matrix

Say we have an n x m matrix with the cells either empty or full. The top left and bottom right cells are both empty. You can move up, down, left, or right. What is the fastest method to see if a path of length <= T of empty cells connects the top left and bottom right corner? I've tried both DFS and BFS with a matrix keeping track of how long it took to get to a particular cell, but both methods were too slow.
EDIT: I don't have access to the code anymore, but here's the pseudocode of what I did.
def find_path(grid, T):
visited = array(grid.dims)
visited.fill(0)
stack = []
stack.append((0, 0, T))
while len(queue) > 0:
pos = stack.pop()
if pos[0] == len(grid) and pos[1] == len(grid[0]):
return True
if pos[2] > 0:
if pos[0] < len(grid)-1 and grid[pos[0]+1][pos[1]] == empty and visited[pos[0]+1][pos[1]] < T-1:
visited[pos[0]+1][pos[1]] = T-1
stack.append(pos[0]+1, pos[1], T-1)
{same for right, left, up}
return False
I'm not quite sure what you're trying to do with the visited array, but "keeping track of how long it took to get to a particular cell" is not going to work with DFS, and is unnecessary with BFS. Probably the problems with your real code are in there somewhere.
The straightforward way to solve this is with a level-by-level BFS, like (in similar pseudocode):
def hasPath(grid,T):
gridcopy = copy(grid)
level=[(0,0)]
for pathlen in 1..T-1:
# Invariant: the positions in level are at path length pathlen
nextlevel=[]
for (x,y) in level:
# each valid direction is north plus maybe southwest and maybe southeast
for sw in 0..1, se in 0..1:
testx = x-sw+se
testy = y-1+sw+se
if testx >= 0 and
testy >= 0 and
testx < grid.width and
testy < grid.height and
gridcopy[testy][testx] == empty:
if testx==grid.width-1 and testy==grid.height-1
return True
nextlevel.append((testx,testy))
gridcopy[testy][testx] = blocked
level = nextlevel
return false

How to go through the elements of a matrix, layer by layer

It is difficult to explain what I want. Lets say I have a matrix of 0 and 1
000000
000000
001100
000000
000000
I want to start from a certain group of ones (this is given in the beginning, and then I want to go outwards.
000000,,,,,,, 000000
011110 OR 001100
010010,,,,,,, 010010
011110,,,,,,, 001100
000000,,,,,,, 000000
The difference is not important, as long as I will go through everything, outwards.
The reason I want to do this is, this matrix of 1 and 0 corresponds to a matrix of some 2D function, and I want to examine the points in that function going outwards. I want to
If i understand the question correctly, basically what you want is to find a group of 1s inside a matrix and invert the group of 1s and all of it's surrounding. This is actually an image-processing problem, so my explanation will be accordingly. Sidenote: the term 'polygon' is here used for the group of 1s in the matrix. Some assumptions made: the polygon is always filled. The polygon doesn't contain any points that are directly at the outer bounds of the matrix (ex.: the point (0 , 2) is never part of the polygon). The solution can be easily found this way:
Step 1: search an arbitrary 1 that is part of the outer bound of the polygon represented by the 1s in the matrix. By starting from the upper left corner it's guaranteed that the returned coordinated will belong to a 1 that is either on the left side of the polygon, the upper-side or at a corner.
point searchArb1(int[][] matrix)
list search
search.add(point(0 , 0))
while NOT search.isEmpty()
point pt = search.remove(0)
//the point wasn't the searched one
if matrix[pt.x][pt.y] == 1
return pt
//continue search in 3 directions: down, right, and diagonally down/right
point tmp = pt.down()
if tmp.y < matrix.height
search.add(tmp)
tmp = pt.right()
if tmp.x < matrix.width
search.add(tmp)
tmp = pt.diagonal_r_d()
if tmp.x < matrix.width AND tmp.y < matrix.height
search.add(tmp)
return null
Step 2: now that the we have an arbitrary point in the outer bound of the polygon, we can simply proceed by searching the outer bound of the polygon. Due to the above mentioned assumptions, we only have to search for 1s in 3 directions (diagonals are always represented by 3 points forming a corner). This method will search the polygon bound clockwise.
int UP = 0
int RIGHT = 1
int DOWN = 2
int LEFT = 3
list searchOuterBound(int[][] matrix , point arbp)
list result
point pt = arbp
point ptprev
//at each point one direction can't be available (determined using the previous found 1
int dir_unav = LEFT
do
result.add(pt)
//generate all possible candidates for the next point in the polygon bounds
map candidates
for int i in [UP , LEFT]
if i == dir_unav
continue
point try
switch i
case UP:
try = pt.up()
break
case DOWN:
try = pt.down()
break
case RIGHT:
try = pt.right()
break
case LEFT:
try = pt.left()
break
candidates.store(i , try)
ptprev = pt
for int i in [0 , 2]
//the directions can be interpreted as cycle of length 4
//always start search for the next 1 at the clockwise next direction
//relatively to the direction we come from
//eg.: dir_unav = LEFT -> start with UP
int dir = (dir_unav + i + 1) % 4
point try = candidates.get(dir)
if matrix[pt.x][pt.y] == 1
//found the first match
pt = try
//direction we come from is the exact opposite of dir
dir_unav = (dir + 2) % 4
break
//no matching candidate was found
if pt == ptprev
return result
while pt != arbp
//algorithm has reached the starting point again
return result
Step 3: Now we've got a representation of the polygon. Next step: Inverting the points around the polygon aswell. Due to the fact that the polygon itself will be filled with 0s later on, we can simply fill up the surrounding of every point in the polygon with 1s. Since there are two options for generating this part of the matrix-state, i'll split up into two solutions:
Step 3.1: Fill points that are diagonal neighbours of points of the polygon with 1s aswell
void fillNeighbours_Diagonal_Included(int[][] matrix , list polygon)
for point p in polygon
for int x in [-1 , 1]
for int y in [-1 , 1]
matrix[p.x + x][p.y + y] = 1
Step 3.1: Don't fill points that are diagonal neighbours of points of the polygon
void fillNeighbours_Diagonal_Excluded(int[][] matrix , list polygon)
for point p in polygon
matrix[p.x - 1][p.y] = 1
matrix[p.x + 1][p.y] = 1
matrix[p.x][p.y - 1] = 1
matrix[p.x][p.y + 1] = 1
Step 4: Finally, last step: Invert all 1s in the polygon into 0s. Note: I'm too lazy to optimize this any further, so this part is implemented as brute-force.
void invertPolygon(int[][] matrix , list polybounds)
//go through each line of the matrix
for int i in [0 , matrix.height]
sortedlist cut_x
//search for all intersections of the line with the polygon
for point p in polybounds
if p.y == i
cut_x.add(p.x)
//remove ranges of points to only keep lines
int at = 0
while at < cut_x.size()
if cut_x.get(at - 1) + 1 == cut_x.get(at)
AND cut_x.get(at) == cut_x.get(at + 1) - 1
cut_x.remove(at)
--at
//set all points in the line that are part of the polygon to 0
for int j in [0 , cut_x.size()[ step = 2
for int x in [cut_x.get(j) , cut_x.get(j + 1)]
matrix[x][i] = 0
I hope you understand the basic idea behind this. Sry for the long answer.

dynamic programming approach in a non-specific direction calculation

I am trying to solve Ball Removal problem on topcoder, pasting the problem statement here as login is required to access this link.
Problem Statement
You have N balls, where N is odd. The balls are numbered from 0 to N-1. In that order, they are arranged into a row going from the left to the right.
In addition to the number, each ball has either the word "left" or the word "right" written on it. For simplicity, we will use the character '<' instead of "left", and the character '>' instead of "right". You are given the labels on all balls as the String label. For each i, character i of label represents the word on ball i.
You will now repeat the following procedure:
Choose a ball that is not at either end of the row of balls.
If the chosen ball has the label '<', remove the chosen ball and also the ball immediately to the left of it. Otherwise, remove the chosen ball and also the ball to the right of it.
Without reordering the remaining balls, push them together to get rid of the gap created in the previous step.
The process ends when only one ball remains in the row. That ball is called the survivor. Note that the numbers on the balls do not change during the process.
Find all possible survivors. Your method must return a String containing exactly N characters. If ball i can be the survivor, character i of the return value must be 'o' (lowercase oh). Otherwise, the corresponding character must be '.' (a period).
Constraints
label will contain between 3 and 49 characters, inclusive.
label will contain an odd number of characters.
Each character of label will be either '>' or '<'.
Examples
"<<>"
Returns: "..o"
Initially, you have three balls. Since you cannot choose balls at the ends of the row, you have to choose ball 1. As its label is '<', you remove balls 0 and 1. Hence the only possible survivor is ball 2.
1)
">>><<"
Returns: "o...o"
If you choose ball 2 or ball 3 first, you have to choose ball 1 next, and the survivor will be ball 0. If you choose ball 1 first, you have to choose ball 3 next, and the survivor will be ball 4.
2)
"<<><<"
Returns: "....o"
3)
"<><<><>"
Returns: "o.....o"
4)
">>><<<>>>>><<<>"
Returns: "o.....o.o.....o"
I am thinking of a dynamic programming approach to this problem, I am thinking of having an boolean array to mark which of the characters have been deleted and then find which is next left and next right but that makes the approach quite inefficient and I have to write a recursive method. For implementing a dynamic programming approach I need to maintain a state. But I am not able to figure out what I should keep as state, in my thinking a state is combination of both current string and current index, but maintaining a string for state doesn't seem correct to me.
One more problem I am facing is that in this case I don't have a particular direction if I change direction result changes also if I move left to right I might need to move right to left also.
Please help me in finding a proper approach to this problem.
The state can be boolean - DP[left][right][isLeftBoundary][isRightBoundary].
This means if the substring starting at left and finishing at right can be fully eliminated.
isLeftBoundary is just a boolean flag if the left symbol is the leftmost of the string.
isRightBoundary is just a boolean flag if the right symbol is the rightmost of the string.
if DP[0][i - 1][1][0] and DP[i + 1][N][0][1] are true, it means the ball at position i can remain.
int canDelete(int l, int r, int st, int en)
{
if (l > r) return 1; //we succeeded in removing the whole string
if (DP[l][r][st][en] != -1)
return DP[l][r][st][en];
int ans = 0;
//i is the last removed ball, which will eliminate the whole string[l, r]
for (int i = l + st; i <= r - en; i++)
{
if (inp[i] == '<') //it will remove a ball to the left, but which one?
{
for (int j = l; j < i; j++) //ball i will remove ball j
{
if (canDelete(l, j - 1, st, 0)
&& canDelete(j + 1, i - 1, 0, 0)
&& canDelete(i + 1, r, 0, en))
ans = 1;
}
}
else
if (inp[i] == '>') //it will remove a ball to the right, but which one?
{
for (int j = i + 1; j <= r; j++) //ball i will remove ball j
{
if (canDelete(l, i - 1, st, 0)
&& canDelete(i + 1, j - 1, 0, 0)
&& canDelete(j + 1, r, 0, en))
ans = 1;
}
}
}
return ans;
}

No of ways to walk M steps in a grid

You are situated in an grid at position x,y. The dimensions of the row is dx,dy. In one step, you can walk one step ahead or behind in the row or the column. In how many ways can you take M steps such that you do not leave the grid at any point ?You can visit the same position more than once.
You leave the grid if you for any x,y either x,y <= 0 or x,y > dx,dy.
1 <= M <= 300
1 <= x,y <= dx,dy <= 100
Input:
M
x y
dx dy
Output:
no of ways
Example:
Input:
1
6 6
12 12
Output:
4
Example:
Input:
2
6 6
12 12
Output:
16
If you are at position 6,6 then you can walk to (6,5),(6,7),(5,6),(7,6).
I am stuck at how to use Pascal's Triangle to solve it.Is that the correct approach? I have already tried brute force but its too slow.
C[i][j], Pascal Triangle
C[i][j] = C[i - 1][j - 1] + C[i - 1][j]
T[startpos][stp]
T[pos][stp] = T[pos + 1][stp - 1] + T[pos - 1][stp - 1]
You can solve 1d problem with the formula you provided.
Let H[pos][step] be number of ways to move horizontal using given number of steps.
And V[pos][step] be number of ways to move vertical sing given number of steps.
You can iterate number of steps that will be made horizontal i = 0..M
Number of ways to move so is H[x][i]*V[y][M-i]*C[M][i], where C is binomial coefficient.
You can build H and V in O(max(dx,dy)*M) and do second step in O(M).
EDIT: Clarification on H and V. Supppose that you have line, that have d cells: 1,2,...,d. You're standing at cell number pos then T[pos][step] = T[pos-1][step-1] + T[pos+1][step-1], as you can move either forward or backward.
Base cases are T[0][step] = 0, T[d+1][step] = 0, T[pos][0] = 1.
We build H assuming d = dx and V assuming d = dy.
EDIT 2: Basically, the idea of algorithm is since we move in one of 2 dimensions and check is also based on each dimension independently, we can split 2d problem in 2 1d problems.
One way would be an O(n^3) dynamic programming solution:
Prepare a 3D array:
int Z[dx][dy][M]
Where Z[i][j][n] holds the number of paths that start from position (i,j) and last n moves.
The base case is Z[i][j][0] = 1 for all i, j
The recursive case is Z[i][j][n+1] = Z[i-1][j][n] + Z[i+1][j][n] + Z[i][j-1][n] + Z[i][j+1][n] (only include terms in the sumation that are on the map)
Once the array is filled out return Z[x][y][M]
To save space you can discard each 2D array for n after it is used.
Here's a Java solution I've built for the original hackerrank problem. For big grids runs forever. Probably some smart math is needed.
long compute(int N, int M, int[] positions, int[] dimensions) {
if (M == 0) {
return 1;
}
long sum = 0;
for (int i = 0; i < N; i++) {
if (positions[i] < dimensions[i]) {
positions[i]++;
sum += compute(N, M - 1, positions, dimensions);
positions[i]--;
}
if (positions[i] > 1) {
positions[i]--;
sum += compute(N, M - 1, positions, dimensions);
positions[i]++;
}
}
return sum % 1000000007;
}

Find the maximum possible area

Given n non-negative integers a1, a2, ..., an, where each represents a
point at coordinate (i, ai). n vertical lines are drawn such that the
two endpoints of line i is at (i, ai) and (i, 0). Find two lines,
which together with x-axis forms a container, such that the container
contains the most water.
Note: You may not slant the container.
One solution could be that we take each and every line and find area with every line. This takes O(n^2). Not time efficient.
Another solution could be using DP to find the maximum area for every index, and then at index n, we will get the maximum area.
I think it's O(n).
Could there be more better solutions?
int maxArea(vector<int> &height) {
int ret = 0;
int left = 0, right = height.size() - 1;
while (left < right) {
ret = max(ret, (right - left) * min(height[left], height[right]));
if (height[left] <= height[right])
left++;
else
right--;
}
return ret;
}
Many people here are mistaking this problem to maximal rectangle problem, which is not the case.
Solution
Delete all the elements aj such that ai >= aj =< ak and i > j < k. This can be done in linear time.
Find the maximum value am
Let as = a1
For j = 2 through m-1, if as >= aj, delete aj, else as = aj
Let as = an
For j = n-1 through m+1, if as >= aj, delete aj, else as = aj
Notice that the resulting values look like a pyramid, that is, all the elements on the left of the maximum are strictly increasing and on the right are strictly decreasing.
i=1, j=n. m is location of max.
While i<=m and j>=m
Find area between ai and aj and keep track of the max
If ai < aj, i+=1, else j-=1
Complexity is linear (O(n))
Here is an implementation with Java:
Basic idea is to use two pointers from front and back, and calculate the area along the way.
public int maxArea(int[] height) {
int i = 0, j = height.length-1;
int max = Integer.MIN_VALUE;
while(i < j){
int area = (j-i) * Math.min(height[i], height[j]);
max = Math.max(max, area);
if(height[i] < height[j]){
i++;
}else{
j--;
}
}
return max;
}
Here is a clean Python3 solution. The runtime for this solution is O(n). It is important to remember that the area formed between two lines is determined by the height of the shorter line and the distance between the lines.
def maxArea(height):
"""
:type height: List[int]
:rtype: int
"""
left = 0
right = len(height) - 1
max_area = 0
while (left < right):
temp_area = ((right - left) * min(height[left], height[right]))
if (temp_area > max_area):
max_area = temp_area
elif (height[right] > height[left]):
left = left + 1
else:
right = right - 1
return max_area
This problem can be solved in linear time.
Construct a list of possible left walls (position+height pairs), in order from highest to lowest. This is done by taking the leftmost possible wall and adding it to the list, then going through all possible walls, from left to right, and taking every wall that is larger than the last wall added to the list. For example, for the array
2 5 4 7 3 6 2 1 3
your possible left walls would be (pairs are (pos, val)):
(3, 7) (1, 5) (0, 2)
Construct a list of possible right walls in the same way, but going from right to left. For the above array the possible right walls would be:
(3, 7) (5, 6) (8, 3)
Start your water level as high as possible, that is the minimum of heights of the walls at the front of the two lists. Calculate the total volume of water using those walls (it might be negative or zero, but that is ok), then drop the water level by popping an element off of one of the lists such that the water level drops the least. Calculate the possible water volume at each of these heights and take the max.
Running this algorithm on these lists would look like this:
L: (3, 7) (1, 5) (0, 2) # if we pop this one then our water level drops to 5
R: (3, 7) (5, 6) (8, 3) # so we pop this one since it will only drop to 6
Height = 7
Volume = (3 - 3) * 7 = 0
Max = 0
L: (3, 7) (1, 5) (0, 2) # we pop this one now so our water level drops to 5
R: (5, 6) (8, 3) # instead of 3, like if we popped this one
Height = 6
Volume = (5 - 3) * 6 = 12
Max = 12
L: (1, 5) (0, 2)
R: (5, 6) (8, 3)
Height = 5
Volume = (5 - 1) * 5 = 20
Max = 20
L: (1, 5) (0, 2)
R: (8, 3)
Height = 3
Volume = (8 - 1) * 3 = 21
Max = 21
L: (0, 2)
R: (8, 3)
Height = 2
Volume = (8 - 0) * 2 = 16
Max = 21
Steps 1, 2, and 3 all run in linear time, so the complete solution also takes linear time.
The best answer is by Black_Rider, however they did not provide an explanation.
I've found a very clear explanation on this blog. Shortly, it goes as follows:
Given array height of length n:
Start with the widest container you can, i.e. from left side at 0 to right side at n-1.
If a better container exists it will be narrower, so its both sides must be higher than the lower of currently chosen sides.
So, change left to (left+1) if height[left] < height[right], otherwise change right to (right-1).
Calculate new area, if it's better than what you have so far, replace.
If left < right, start over from 2.
My implementation in C++:
int maxArea(vector<int>& height) {
auto current = make_pair(0, height.size() - 1);
auto bestArea = area(height, current);
while (current.first < current.second) {
current = height[current.first] < height[current.second]
? make_pair(current.first + 1, current.second)
: make_pair(current.first, current.second - 1);
auto nextArea = area(height, current);
bestArea = max(bestArea, nextArea);
}
return bestArea;
}
inline int area(const vector<int>& height, const pair<int, int>& p) {
return (p.second - p.first) * min(height[p.first], height[p.second]);
}
This problem is a simpler version of The Maximal Rectangle Problem. The given situation can be view as a binary matrix. Consider the rows of the matrix as X-axis and columns as Y-axis. For every element a[i] in the array, set
Matrix[i][0] = Matrix[i][1] = ..... = Matrix[i][a[i]] = 1
For e.g - For a[] = { 5, 3, 7, 1}, our binary matrix is given by:
1111100
1110000
1111111
1000000

Resources