Let's take this map, where '#' illustrates a taken square and '.' illustrates a free square:
1 . # # # . .
2 . # . . # .
3 # . . . . #
4 . # # # . .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6
Now, if I put a '#' in the square 4,5 the area would be "filled" like this:
1 . # # # . .
2 . # # # # .
3 # # # # # #
4 . # # # # .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6
So, what is the best way to find "a limited square", where I can start flood fill or other filling algorithm that fills the limited area?
If you can convert your problem to a graph, what you are looking for is to identify connected components. And if a connected component does not contain an edge that is the boundary edge, then you have found the region that needs to be filled.
If I define the graph like this:
G = (V, E)
V = {r1, r2, r3, r4, r5, r6, c1, c2, c3, c4, c5, c6}
E = {(u, v) | u, v are elements of V && the cell(u, v) is not taken}
Now run DFS to find all disconnected trees. Algorithm:
for each u in V:
color[u] = white
for each u in V:
if color[u] == white:
contains_boundary_edge = False
DFS-visit( u, contains_boundary_edge )
if not contains_boundary_edge:
Flood-fill( u )
DFS-visit( u, contains_boundary_edge ):
color[u] = gray
for each v in adjacent( u ):
if color[v] == white:
if edge(u, v) is a boundary edge: // Can be easily identified if one of u, v is start or end row/col.
contains_boundary_edge = True
DFS-visit( v, contains_boundary_edge )
color[u] = black
I think this question can be reduced to a convex hull problem if we consider each # as point x,y then convex hull be give us the x,y of all the # which are absent
http://en.wikipedia.org/wiki/Convex_hull
I will try to code it in leisure ..
You could attack this by processing each '.' node.
Definition: A '.' node is enclosed if there does not exist a path from the node to the boundary of the map.
If you agree with the above definition, the algorithm would be to maintain a graph of '.' nodes, where adjacent nodes are connected.
Every time a node is changed to '#', remove it from this graph, and check each remaining '.' node to see if a path exists from it to one of the nodes on the map border.
Depending on the size of your map, you made need to attempt various optimizations to limit the number of path searches performed each turn.
If you model this map as a graph, and each square is connected to its four neighbours, you can use a bridge finding algorithm to find the square you need.
Note this model gives you several subgraphs to work with sometimes, so it might produce a number of false positives around the border, since adding a # there would certainly separate some nodes from the rest. To get around this, you could pad two levels of squares around the graph, so that no single # could separate a border node from the rest.
#svick's comment inspired this method.
I would start from each neighbor of the picked square, and try to 'escape' to the boundary of the grid. Meanwhile, mark the path followed by 'X'. If you can escape: undo every 'X'. If you cannot escape, replace every 'X' by '#'. I made an example in Java, as shown below.
int W, H;
char[][] input;
final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public void handle(int x, int y) {
// try each neihgbor
for (int[] d : directions) {
if (canEscape(input, x, y)) {
// if we can escape, the path found shouldn't be filled
// so replace the Xes by '.';
handleXes(input, false);
} else {
// if we cannot escape, this is a closed shape, so
// fill with '#'
handleXes(input, true);
}
// note that this can be written more concisely as
// handleXes(input, !canEscape(input, x, y));
}
}
public boolean canEscape(char[][] grid, int x, int y) {
if (isEscape(grid, x, y))
return true
if (isValid(grid, x, y)) {
// mark as visited
grid[x][y] = 'X';
// try each neighbor
for (int[] d : directions) {
if (canEscape(grid, x+d[0], y+d[1]))
return true;
}
}
return false;
}
public boolean isValid(char[][] grid, int x, int y) {
return 0 <= x && x < W && 0 <= y && y < H && grid[x][y] == '.';
}
public boolean isEscape(char[][] grid, int x, int y) {
return (0 == x || x == W-1 || 0 == y || y == H-1) && grid[x][y] == '.';
}
public void handleXes(char[][] grid, boolean fill) {
for (int x = 0; x < W; x++)
for (int y = 0; y < H; y++)
if (grid[x][y] == 'X')
grid[x][y] = fill ? '#' : '.';
}
Related
I have a problem where I have a grid of points, the vertices obstructions and a starting point
I need to determine the shortest, straight line path between the starting point and each point in the grid. Of note, the points are not a graph, so traversal does not need to be graph-like such as with A* or Dijkstra. That is to say, given the following grid:
S 1 2 3
4 5 6 7
8 x 9 10
11 x 13 14
Where S is the starting point, E is the ending point, x is an obstruction and any number represents a point (consider it a waypoint), I need to determine the shortest path to reach each numbered point from S. For straight lines, this is easy, but to find the points such as 13, the path can be S - 9 - 13 and not S - 5 - 9 - 13.
The reasoning is that this problem will model flights, which don't necessarily have to abide by traveling a gride in 8 possible directions, and can fly over portions of cells; the points here representing the center of each cell.
I'm not asking for an implementation, just if a well known algorithm for such a problem exists.
My current design is based on finding the initial set of visible points from S, then building a subset of the non-visible points. From there, find the furthest point from S that is the closest to the set of points that cannot be seen, and repeat.
This is quite interesting problem, didn't know that can have so practical application. This answer is in Java, because I am most familiar in it, but algorithm is quite simple and comments in the code should help you to reimplement in any language.
I have extended your data grid with one more row, and added E (end position). So the complete grid looks like:
S 1 2 3
4 5 6 7
8 x 9 10
11 x 13 14
12 x E 15
Just few remarks:
Each cell has dimension 1 by 1. It means that each cell has diagonal equal to sqrt(2), because of d^2 = 1^2 + 1^2
Each cell is called Position(value, row, column). Row and Column values are like coordinates, and Value is just a value in the cell. So for example our start is: new Position("S", 0, 0) and end is new Position("E", 4, 2)
The proposed algorithm comes after some "shortest path algorithm", like mentioned A* or Dijkstra
The proposed algorithm looks only at the angles of the shortest path, if values on the shortest path should be considered in computation, then I could replace it with backtracking mechanism.
Algorithm setup:
// shortest path is stored in "positions" list
Position _S = new Position("S", 0, 0);
Position _5 = new Position("5", 1, 1);
...
Position _E = new Position("E", 4, 2);
List<Position> positions = new ArrayList<>();
Collections.addAll(positions, _S, _5, _9, _13, _E);
// obstruction points are stored in "xxx" list
Position x1 = new Position("x", 2, 1);
Position x2 = new Position("x", 3, 1);
Position x3 = new Position("x", 4, 1);
List<Position> xxx = new ArrayList<>();
Collections.addAll(xxx, x1, x2, x3);
Now the algorithm:
// iterate from the end
int index = positions.size()-1;
while (index > 1) {
// get three current positions
Position c = positions.get(index);
Position b = positions.get(index-1);
Position a = positions.get(index-2);
// calculate angle on point "b". Angle is constructed with a-b-c
int angle = angle(a,b,c);
if (angle == 0) { // angle = 0 means that line is straight
positions.remove(index-1); // then just remove the middle one (b)
} else { // in ELSE part check if line goes through any "x" cell
Line line = new Line(a,c);
boolean middleCanBeRejected = true;
for (Position x : xxx) {
if (line.distanceTo(x) < HALF_DIAGONAL) { // if distance from "x" to "line" is less than sqrt(2),
// it means that straight line will pass over "x",
// so we are not able to discard the middle point
middleCanBeRejected = false;
break;
}
}
if (middleCanBeRejected) { // still in ELSE
positions.remove(index-1); // check result of FOR above
}
}
index--;
}
Finally the test cases:
S 1 2 3
4 5 6 7
8 x 9 10
11 x 13 14
12 x E 15
Path:
S 0.0, 0.0
9 2.0, 2.0
E 4.0, 2.0
S 1 2 3
4 5 6 7
8 22 9 10
11 23 13 14
12 x E 15
Path:
S 0.0, 0.0
E 4.0, 2.0
Appendix, the utility methods and classes:
// -1 -> turn right
// 0 -> straight
// +1 -> turn left
// Point2D.ccw() algorithm from Sedgewick's book
public static int angle(Position a, Position b, Position c) {
double area = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
if (area < 0.0D) { return -1; }
else { return area > 0.0D ? 1 : 0; }
}
public class Line {
// A*x + B*y + C = 0
double A;
double B;
double C;
public Line(Position p1, Position p2) {
// https://math.stackexchange.com/questions/422602/convert-two-points-to-line-eq-ax-by-c-0
this.A = p1.y - p2.y;
this.B = p2.x - p1.x;
this.C = p1.x*p2.y - p2.x*p1.y;
}
public double distanceTo(Position p) {
double nom = Math.abs(A*p.x + B*p.y + C);
double den = Math.sqrt(A*A + B*B);
return nom/den;
}
}
public class Position {
String value;
double x; // row
double y; // column
public Position(String value, double x, double y) {
this.value = value;
this.x = x;
this.y = y;
}
}
I'm trying to calculate the maximum sum that can be achieved in going from left column to right column in a grid. Allowed movements are up, down, right. I've implemented this solution (it's Breadth First Search) :
for(int i=1; i<=n; i++) {
Queue<Position> q = new LinkedList<Position>();
q.add(new Position(i, 1));
dp[i][1] = map[i][1];
while(!q.isEmpty()) {
Position node = q.poll();
visited[node.n][node.m] = 1;
if(dp[node.n][node.m] > max) {
max = dp[node.n][node.m];
}
if(visited[node.n-1][node.m] != 1 && node.n != 1 && dp[node.n-1][node.m] < dp[node.n][node.m] + map[node.n-1][node.m] && map[node.n-1][node.m] != -1) {
dp[node.n-1][node.m] = dp[node.n][node.m] + map[node.n-1][node.m];
q.add(new Position(node.n-1, node.m));
}
if(visited[node.n+1][node.m] != 1 && node.n != n && dp[node.n +1][node.m] < dp[node.n][node.m] + map[node.n+1][node.m] && map[node.n+1][node.m] != -1) {
dp[node.n +1][node.m] = dp[node.n][node.m] + map[node.n+1][node.m];
q.add(new Position(node.n + 1, node.m));
}
if(visited[node.n][node.m+1] != 1 && node.m != m && dp[node.n][node.m+1] < dp[node.n][node.m] + map[node.n][node.m+1] && map[node.n][node.m+1] != -1) {
dp[node.n][node.m+1] = dp[node.n][node.m] + map[node.n][node.m+1];
q.add(new Position(node.n, node.m+1));
}
}
}
static class Position {
int n, m;
public Position(int row, int column) {
this.n = row;
this.m = column;
}
}
Example Input:
-1 4 5 1
2 -1 2 4
3 3 -1 3
4 2 1 2
The problem with my solution is it should reach 2 (in last row 2nd column) by following 4->3->3->2 but my solution put 2 in visited state so it won't check it. And if I remove visited array, it will get trapped in infinite loop of up, down, up, down on any cell.
Edit : Each point can be visited only once.
This problem can be solved with a linear programming approach, but there is a small twist because you cannot visit each cell more than once but the movements can actually take you to that condition.
To solve the issue you can however note that in a given position (x, y) you either
just arrived at (x, y) from (x-1, y) and therefore you are allowed to go up, down or right (unless you're on the edges, of course)
arrived at (x, y) from (x, y-1) (i.e. from above) and then you're allowed only to go down or right
arrived at (x, y) from (x, y+1) (i.e. from below) and then you're allowed only to go up or right
This translates directly in the following recursive-memoized solution (code is in Python):
matrix = [[-1, 4, 5, 1],
[ 2,-1, 2, 4],
[ 3, 3,-1, 3],
[ 4, 2, 1, 2]]
rows = len(matrix)
cols = len(matrix[0])
cache = {}
def maxsum(dir, x, y):
key = (dir, x, y)
if key in cache: return cache[key]
base = matrix[y][x]
if x < cols-1:
best = base + maxsum("left", x+1, y)
else:
best = base
if dir != "above" and y > 0:
best = max(best, base + maxsum("below", x, y-1))
if dir != "below" and y < rows-1:
best = max(best, base + maxsum("above", x, y+1))
cache[key] = best
return best
print(max(maxsum("left", 0, y) for y in range(rows)))
If you are not allowed to step over a negative value (even if that would guarantee a bigger sum) the changes are trivial (and you need to specify what to return if there are no paths going from left column to right column).
I am trying to develop an application that maps my office (exactly like application like google maps, showing path from one seat to another).
From what I have read so far, algorithms like Dijkstra's, or Back Tracking can be used to solve the problem. But these algorithms require something of a 2 D matrix (or a variant of that) as an input. Now thinking from an administrative point of view of application, the person has only the floor map of office to feed as input to the application. How can this floor map be transposed to something which these algorithms can take as input? Or am I missing something altogether?
Any suggestion would be appreciated.
Actually the matrix isn't required. The graph can just aswell be generated on runtime. The image can be converted into a map of walls and open spots by simply converting it into a two-colored image, where one color represents walls. This would look like this for your requirements:
define node: (int x , int y)
define isWall:
//this method is actually used for imageprocessing
//i've forgotten the name of it, so if anyone knows it, pls comment
input: int rgb
output: boolean wall
int red = red(rgb)
int green = green(rgb)
int blue = blue(rgb)
int maxWallVal//comparison value
return (red + green + blue) / 3 < maxWallVal
define listNeighbours:
input: node n , int[][] img
output: list neighbours
int x = n.x
int y = n.y
list tmp
if x + 1 < img.length
add(tmp , (x + 1 , y)
if x > 0
add(tmp , (x - 1 , y)
if y + 1 < img[x].length
add(tmp , (x , y + 1)
if y > 0
add(tmp , (x , y - 1)
for node a in tmp
int rgb = img[a.x][a.y]
boolean wall = isWall(rgb)
if NOT wall
add(neighbours , a)
return neighbours
define findPath:
input: node start , node end , int[][] img
output: list path
set visited
map prevNodes
queue nodes
add(nodes , start)
while NOT isEmpty(nodes)
node n = remove(0 , nodes)
if n == end
break
add(visited , nodes)
for node a in listNeighbours(n)//list all neighbour-fields that are no wall
if contains(visited , a)
continue
add(queue , a)
put(n , a)
node tmp = start
while tmp != null
add(path , tmp)
tmp = get(prevNodes , tmp)
return path
I have implemented my A* algorithm such that it finds the shortest path to goal, given that it can move only to neighboring/adjacent cells. (Assume nodes are cells in a grid). So there's 8 surrounding cells it can move.
OOO
O*O
OOO
This works and finds the shortest path but what if I only want the nodes that are about 20-40 cells apart, and less if needed (say a door is only one cell away. How would I go about this without post processing the path?
So something like this?
XXXXOXOXOXXXX
XXXXXOOOXXXXX
OOOOOO*OOOOOO
XXXXXOOOXXXXX
XXXXOXOXOXXXX
Where O are the 'neighbors and X's are not.
def search(grid,start,goal):
dist = {}
visited = {}
predecessors= {}
hscore = {}
gscore = {}
gscore[start] = 0
hscore[start] = gscore[start] + heuristic(start,goal)
pq = PriorityQueue()
pq.put((hscore[start],start))
while not pq.empty():
current = pq.get()
if current[1] == goal:
return build_path(predecessors,goal)
visited[current[1]] = current[1]
max_dist = 0
succs = successors(grid,current[1],goal,pixels=90)
for successor in succs:
if visited.get(successor[0],None) != None:
continue
grid.grid[successor[0]] = (0,255,0)
g = gscore[current[1]] + 1
in_pq = successor[0] in pq
if (not in_pq or g < gscore[successor[0]] ):
predecessors[successor[0]] = current[1]
gscore[successor[0]] = g
hscore[successor[0]] = g + heuristic(successor[0],goal)
max_dist = dist
if not in_pq:
pq.put((hscore[successor[0]],successor[0]))
return []
I've got a specific type of the planar graph and I found it interesting to search for an algorithm which will color its vertices legally. About this type of graph, it's very easy and cool:
Consider any tree T with n>2 vertices and k leaves. Let's denote G(T) as a graph constructed from T by connecting its leaves into k-cycle in such way that G(T) is planar.
And the problem I came up with is to color G(T) with 3 colors. Clearly, G(T) as a planar graph, is 4-colorable, but I think (don't have a proof) that it is almost always 3-colorable due to its simplicity. Almost always means that only if T is a star and only with odd number of leaves, then G(T) is 4-colorable.
I am looking for some algorithm, or maybe proof of my assumptions which could be easily transformed into an algorithm. I would be very very grateful for any help, hints.
In case I wasn't clear enough I'll give an example:
Let T be a tree with edges E(T) = { {1,2}, {2,3}, {2,4}, {4,5} } and then E(G(T)) = sum of the sets: E(T) and { {1,5}, {5,3}, {3,1} }, since we are connecting leaves 1,5,3 into a cycle.
local Edges_of_Tree = {{1,2}, {2,3}, {2,4}, {4,5}}
local Cycle_of_Leaves = {1,5,3}
local color1 = 'Red'
local color2 = 'Green'
local color3 = 'Blue'
local vert = {}
local vert_arr = {}
local function add_edge(v1, v2)
assert(v1 ~= v2)
if not vert[v1] then
vert[v1] = {deg = 0, adj = {}}
table.insert(vert_arr, v1)
end
vert[v1].deg = vert[v1].deg + 1
assert(not vert[v1].adj[v2], 'multiple edges between '..v1..' and '..v2)
vert[v1].adj[v2] = true
end
for _, edge in ipairs(Edges_of_Tree) do
local v1, v2 = unpack(edge)
add_edge(v1, v2)
add_edge(v2, v1)
end
table.sort(vert_arr)
local leaf_ctr = 0
local root
for v, vv in pairs(vert) do
if vv.deg == 1 then
leaf_ctr = leaf_ctr + 1
else
root = v
end
end
assert(#vert_arr > leaf_ctr + 1, 'tree is a star')
assert(leaf_ctr == #Cycle_of_Leaves, 'invalid Cycle_of_Leaves')
for _, v in ipairs(Cycle_of_Leaves) do
assert(vert[v] and vert[v].deg == 1 and vert[v].color == nil,
'invalid Cycle_of_Leaves')
vert[v].color = false
end
local function recursive_paint_inodes(v, color, prev_v)
assert(vert[v].color == nil, 'a cycle in tree found')
vert[v].color = color
local next_color = (color1..color2):gsub(color, '')
for next_v in pairs(vert[v].adj) do
if next_v ~= prev_v and vert[next_v].deg > 1 then
recursive_paint_inodes(next_v, next_color, v)
end
end
end
recursive_paint_inodes(root, color1)
local front
for i = 1, leaf_ctr do
local vv = vert[Cycle_of_Leaves[i]]
vv.next = Cycle_of_Leaves[i % leaf_ctr + 1]
vv.prev = Cycle_of_Leaves[(i - 2) % leaf_ctr + 1]
local parent = next(vv.adj)
if parent ~= next(vert[vv.prev].adj) then
assert(not vert[parent].conn_to_leaf, 'graph is non-planar')
vert[parent].conn_to_leaf = true
front = Cycle_of_Leaves[i]
end
end
vert[next(vert[vert[front].prev].adj)].color = color3
vert[front].color = color3
local tricolor = color1..color2..color3
local leaf = front
for i = 1, leaf_ctr - 1 do
local prev_color = vert[leaf].color
leaf = vert[leaf].next
local parent_color = vert[next(vert[leaf].adj)].color
local enabled_colors = tricolor:gsub(prev_color, ''):gsub(parent_color, '')
vert[leaf].color = enabled_colors:match(color1)
or enabled_colors:match(color2) or color3
end
for _, v in ipairs(vert_arr) do
print(v..' '..vert[v].color)
end
This code is written in Lua.
You can test it in action there.