Suppose you have an NxN maze with a Knight, Princess and Exit.
There is also an evil Witch that is planning to block M squares (set them on fire). She will set all these blocks on fire before the Knight makes his first move (they do not alternate turns).
Given the map to the maze, and M, can you decide in O(N^2) whether the Knight will be able to reach the princess, and then the exit, for any choice of blocks by the Witch (meaning - can the Witch make choices that would prevent the Knight & Princess from escaping)?
This problem seems to be equivalent to determining if there exists M + 1 distinct paths from the knight to the princess, and M + 1 distinct paths from the princess to the exit. If there are only M distinct paths from the knight to the princess (or princess to exit), the witch can just burn one square from each path, blocking the rescue (and, alas, any chance of a happily-ever-after romance between them).
For example, the maze in your question has two distinct paths from the knight to the princess, and two distinct paths from the princess to the exit. Thus, the which can burn min(2, 2) to prevent escape.
The number of distinct paths between two points can be found by using a maximal network flow algorithm. Each cell in the grid is a node in the network; two nodes have an edge (of capacity 1) connecting them if they are adjacent and both white. The maximal network flow from the one point to another represents the number of distinct paths between them.
The Ford Fulkerson algorithm will solve the network flow problem in O(E * f) time, where E is the number of edges in the network (at most N^2) and f is the value of the maximum network flow. Because the maximum network flow is at most 4 (the knight only has four possible directions for his first move), the total complexity becomes O(2 * E * 4) = O(N^2), as requested.
Avoiding using a node more than once
As others have pointed out, the above solution prevents edges going into and out of nodes being used more than once; not the nodes themselves.
We can modify the flow graph to avoid nodes being used more than once by giving each cell four input edges, a single guard edge, and four output edges (each having a weight of 1) as follows:
The output edge of one cell corresponds to the input of another. Each cell can now only be used for one path, as the guard edge can only have a flow of 1. Sink and source cells remain unchanged. We still have a constant number of edges per cell, leaving the complexity of the algorithm unchanged.
Related
I'm solving the problem described below and can't think of a better algorithm than trying every permutation of every vertex of every group with every.
I'm given a graph of vertices, along with a list of groups of specific vertices, the goal is to find the shortest path from a specific starting vertex to a specific ending vertex, and the path must pass through at least one vertex from each specified group of vertices.
There are also vertices in the graph that are not part of any given group.
Re-visiting vertices and edges is possible.
The graph data is specified as follows:
Vertex list - each vertex is identified by a sequence number (0 to the number of vertices -1 )
Edge list - list of vertex pairs (by vertex number)
Vertex group list - list of lists of vector numbers
A specific starting and ending vertex.
I would be grateful for any ideas for a better solution, thank you.
Summary:
We can use bitmasks to efficiently check which groups we have visited so far, and combine this with a traditional BFS/ Dijkstra's shortest-path algorithm.
If we assume E edges, V vertices, and K vertex-groups that have to be included, the below algorithm has a time complexity of O((V + E) * 2^K) and a space complexity of O(V * 2^K). The exponential 2^K term means it will only work for a relatively small K, say up to 10 or 20.
Details:
First, are the edges weighted?
If yes then a "shortest path" algorithm will usually be a variation of Dijkstra's algorithm, in which we keep a (min) priority queue of the shortest paths. We only visit a node once it's at the top of the queue, meaning that this must be the shortest path to this node. Any other shorter path to this node would already have been added to the priority queue and would come before the current iteration. (Note: this doesn't work for negative paths).
If no, meaning all edges have the same weight, then there is no need to maintain a priority queue with the shortest edges. We can instead just run a regular Breadth-first search (BFS), in which we maintain a deque with all nodes at the current depth. At each step we iterate over all nodes at the current depth (popping them from the left of the deque), and for each node we add all it's not-yet-visited neighbors to the right side of the deque, forming the next level.
The below algorithm works for both BFS and Dijkstra's, but for simplicity's sake for the rest of the answer I'll pretend that the edges have positive weights and we will use Dijkstra's. What is important to take away though is that for either algorithm we will only "visit" or "explore" a node for a path that must be the shortest path to that node. This property is essential for the algorithm to be efficient, since we know that we will at most visit each of the V nodes and E edges only one time, giving us a time complexity of O(V + E). If we use Dijkstra's we have to multiply this with log(V) for the priority queue usage (this also applies to the time complexity mentioned in the summary).
Our Problem
In our case we have the additional complexity that we have K vertex-groups, for each of which our shortest path has to contain at least one the nodes in it. This is a big problem, since it destroys our ability to simple go along with the "shortest current path".
See for example this simple graph. Notation: -- means an edge, start is that start node, and end is the end node. A vertex with value 0 does not have a vertex-group, and a vertex with value >= 1 belongs to the vertex-group of that index.
end -- 0 -- 2 -- start -- 1 -- 2
It is clear that the optimal path will first move right to the node in group 1, and then move left until the end. But this is impossible to do for the BFS and Dijkstra's algorithm we introduced above! After we move from the start to the right to capture the node in group 1, we would never ever move back left to the start, since we have already been there with a shorter path.
The Trick
In the above example, if the right-hand side would have looked like start -- 0 -- 0, where 0 means the vertex does not not belonging to a group, then it would be of no use to go there and back to the start.
The decisive reason of why it makes sense to go there and come back, although the path will get longer, is that it includes a group that we have not seen before.
How can we keep track of whether or not at a current position a group is included or not? The most efficient solution is a bit mask. So if we for example have already visited a node of group 2 and 4, then the bitmask would have a bit set at the position 2 and 4, and it would have the value of 2 ^ 2 + 2 ^ 4 == 4 + 16 == 20
In the regular Dijkstra's we would just keep a one-dimensional array of size V to keep track of what the shortest path to each vertex is, initialized to a very high MAX value. array[start] begins with value 0.
We can modify this method to instead have a two-dimensional array of dimensions [2 ^ K][V], where K is the number of groups. Every value is initialized to MAX, only array[mask_value_of_start][start] begins with 0.
The value we store at array[mask][node] means Given the already visited groups with bit-mask value of mask, what is the length of the shortest path to reach this node?
Suddenly, Dijkstra's resurrected
Once we have this structure, we can suddenly use Dijkstra's again (it's the same for BFS). We simply change the rules a bit:
In regular Dijkstra's we never re-visit a node
--> in our modification we differentiate by mask and never re-visit a node if it's already been visited for that particular mask.
In regular Dijkstra's, when exploring a node, we look at all neighbors and only add them to the priority queue if we managed to decrease the shortest path to them.
--> in our modification we look at all neighbors, and update the mask we use to check for this neighbor like: neighbor_mask = mask | (1 << neighbor_group_id). We only add a {neighbor_mask, neighbor} pair to the priority queue, if for that particular array[neighbor_mask][neighbor] we managed to decrease the minimal path length.
In regular Dijkstra's we only visit unexplored nodes with the current shortest path to it, guaranteeing it to be the shortest path to this node
--> In our modification we only visit nodes that for their respective mask values are not explored yet. We also only visit the current shortest path among all masks, meaning that for any given mask it must be the shortest path.
In regular Dijkstra's we can return once we visit the end node, since we are sure we got the shortest path to it.
--> In our modification we can return once we visit the end node for the full mask, meaning the mask containing all groups, since it must be the shortest path for the full mask. This is the answer to our problem.
If this is too slow...
That's it! Because time and space complexity are exponentially dependent on the number of groups K, this will only work for very small K (of course depending on the number of nodes and edges).
If this is too slow for your requirements then there might be a more sophisticated algorithm for this that someone smarter can come up with, it will probably involve dynamic programming.
It is very possible that this is still too slow, in which case you will probably want to switch to some heuristic, that sacrifices accuracy in order to gain more speed.
You are given an n x n chessboard with k knights (of the same color) on it. Someone has spilled superglue on k of the squares, and if a knight ever finishes his move on one of these glue squares, it becomes stuck forever. Additionally (and this is why we can't have nice things) someone has cut out some of the squares so the chessboard has holes in it. You are given an initial position of the knights. The knights move as they do in regular chess, but unlike regular chess, on each turn all the knights move at once (except of course the stuck ones). At the end of each move, a square cannot be occupied by more than 1 knight. Hole squares can't be occupied by knights either (but they do count as squares that the knight can jump over). Give an 0(t x poly(n))-time algorithm to determine whether you can use < t moves to move all the knights from their initial positions to new positions where they are each stuck at a glue square.
My initial thought is to formulate this problem into a maximum flow problem and use Ford-Fulkerson algorithm to solve it. But I am not sure what my nodes and edges should be. Any idea? Thanks!
The described problem can be modeled as a layered network problem as follows. The node set of the network consists of an artificial starting node s and an artificial terminal node t. The intermediate node set consists of k copies of the n * n chessboard, which means that there are
2 + k * n * n
nodes in total. Imagine s at the top, followed by the k layers of the chessboard copies. The terminal node t would be at the bottom.
Connect s to the initial knight positions in the first chessboard and connect t to all desired terminal positions of the knights in the k-th chessboard.
For every i in {1,...,k-1} connect each square in the i-th chessboard to every square in the i+1the chessboard if and only if it can be reached by a legal knight's move. Finally, delete all edges which leave a superglued square (except if t is its tail) and delete all edges which lead to a hole. Furthermore, every edge is constrained to permit a flow of at least 0 and at most 1. In total, the network has at most
2 * k + k * n * n = k * ( 2 + n * n )
edges. To furthermore take into account that every square is to be occupied by at most one knight, the flow in every intermediate node must also be constrained by 1. This can be done by expanding each intermediate node into two nodes and connecting them by an additional edge in which the flow is constrained by 1, which causes the set of nodes and edges to grow by a factor of at most 2.
The k knights can be moved from their initial positions to their terminal positions if and only if the network admits an s-t-flow of value k, where the sequence of knight's movements and the realizing network flows bijectively correspond.
In a directed graph, find the shortest path from s to t such that the path passes through a certain subset of V, let's call them death nodes. The algorithm is given a number n, while traversing from s to t, the path cannot pass though more than n death nodes. What is the best way to find the shortest path, her? I am thiniing Dijkstra's, but how to make sure we are not passing though more than n nodes? Please help me tweak Dijkstra's to include this condition.
Small n
If n is small you can make n copies of your graph, call them levels 1 to n.
You start at s in level 1. If you are at a normal node, the edges take you to nodes within the same level. If you are at a death node, the edges take you to nodes within the next level. If you are at a death node on level n, the edges are simply omitted.
Also connect the t nodes at all levels to a new single destination T (with zero weight).
Then compute the shortest path from s to T.
The problem with this approach is that the graph size goes up by a factor of n, so it is only appropriate for small n.
Large n
An alternative approach is to increase the weight for each edge leaving a death node by a variable x.
As you increase the variable x, the shortest path will use fewer and fewer death nodes. Adjust the value for x (e.g. with bisection) until the graph only uses n death nodes.
This should take around O(logn) evaluations of the shortest path.
I'd add the number of dead nodes encountered on the way as a new (sparse) dimension to the computed distance -- basically you'd have up to n best distances per node.
Implementing your own BFS would be similar: You'll need to treat "seen with x dead nodes" different from "seen with y dead nodes" for each node, unless the total distance and number of dead nodes on the way are both smaller.
p.s.: If you get stuck with this approach, please post code so far O:)
This is a problem from Algorithm Design book.
Given a bipartite graph with vertices G=(V,E) where V=(A,B) such that |A|=|B|=n.
We manage to perfectly match n-2 nodes in A to n-2 nodes in B. However, for the remaining two nodes in A we map them both to a certain node in B (not one of the n-2 nodes in B that are already matched to.)
Given the information from the "matching" above, how to use O(n^2) time to decide whether a perfect matching between A and B actually exists? A hint is fine. Thank you.
Let's have u and v be the two nodes in A that match to the same node x in B. Pick one of those two nodes - call it u - and remove the edge to x from the matching. You are now left with a graph where you have a matching between n - 1 of the nodes from A and n - 1 of the nodes from B. The question now is whether you can extend this matching to make it even bigger.
There's a really nice way to do this using Berge's theorem, which says that a matching in a graph is maximum if and only if there is no alternating path between two unmatched nodes. (An alternating path is one that alternates between using edges not included in the matching and edges included in the matching). You can find a path like this by starting from the node u and trying to find a path to x by doing a modified binary search, where when you go from A to B you only follow unmatched edges and when you go from B back to A you only follow matched edges. If an alternating path exists from u to x, then you'll be sure to find it this way, and if no such path exists, then you can be certain of that as well.
If you do find an alternating path from u to x, you can "flip" it to increase the size of the matching by one. Specifically, take all the edges in the path that aren't in the matching and add them in, and take all the edges that were in the matching and delete them. The resulting is still a valid matching that has one more edge in it than what you started with (if you don't see why this is, play around with some examples and see what you find, or look at the proof of Berge's theorem).
Overall, this approach will require time O(m + n), where m is the number of edges in the graph and n is the number of nodes. The number of edges m is at most O(n2) in a bipartite graph, so this matches your time bound (and, in fact, is actually a bit tighter!)
Transform this problem to the max flow min cut problem by adding a source s which is connected to A by unit capacity edges and a sink t to which B is connected by unit capacity edges.
As templatetypedef said in their answer, we already have a flow of size n-1 on this network.
The problem is now to determine whether the size of the flow can be increased to n. This can be achieved by running one round of Edmonds-Karp heuristic which takes O(E)=O(n^2) time (i.e find the shortest path in the residual graph of the flow of size n-1 above and look for the bottleneck edge.)
Here is the full title I would have posted, but it happens to be too long:
Given a source node, dest node, and intermediate nodes, how does one detect if the shortest Manhattan Distance is blocked by the intermediate nodes?
I've drawn a diagram to make it more clear. On the left side, "u" is the source node and "v" is the destination node. The nodes labeled 1 through 6 are the intermediate nodes. The shortest Manhattan Distance from u -> v would be 12, but the intermediate nodes form a wall blocking it. The diagram on the right, with u' being the source, and v' being the destination, shows that the intermediate nodes 1 through 5 do not block the shortest Manhattan distance from u' to v'.
I'm trying to find an algorithm that won't require me to actually do a graph search (e.g. BFS), because the distance between u and v could potentially be very large.
If all you want to do is detect whether the shortest path (one consisting of moves that monotonically take you in the right direction) is blocked, then you are trying to check whether the blocking nodes cut the rectangle whose corners are given by the source and destination node into two different regions that are disconnected. If no shortest path from the source to the destination is possible, then every path must have some point in it that's blocked.
Let's suppose for simplicity that your start point is below and to the left of the destination point. In that case, find, in O(n), all of the other points that are obstacle points contained in the bounding box holding the start and end point. You now want to see if there is some subset of those nodes that cuts the rectangle into two pieces, one containing the bottom-left corner and one containing the top-right corner. This is possible iff there is a path of the blocking nodes from the left side to the right side, from the left side to the bottom side, from the top side to the right side, or from the top side to the bottom side. Thus we just need to check if any of these are possible.
Fortunately, this can be done efficiently by modeling the problem as a graph search in a graph that has size O(n), where n is the number of blocking points, and has nothing to do with the size of the bounding box. That is, no matter how far apart the test points are, the size of the graph to search depends solely on the number of blocking points.
The graph you want to construct has two parts. First, build a graph where each blocking point is connected to each other blocking point in the 3x3 square surrounding it. These edges link together blocking points that could be part of the same barrier, in that no path from the source to the target could pass between two blocking points joined by an edge. Now, add in four new nodes representing the top wall, left wall, right wall, and bottom wall and connect them to each node that is adjacent to the appropriate wall. That way, for example, a path from the left wall node to the right wall node would represent a series of blocking nodes that make it impossible to get from the bottom-left node to the top-right node.
This graph has size O(n), where n is the number of blocking nodes, since there are O(n) nodes and each node can have at most 12 edges - one for each of the 8 neighbors and potentially one for each of the four walls. You could construct it in at worst quadratic time by scanning over each node and, for each other node, seeing if they are adjacent. There is probably a better way to do this, but nothing comes to me at the moment.
Now that you have the graph, for each of the pairs of walls that, if connected, would disconnect the graph, run a graph search in this graph between those two wall nodes. If a path exists, report that the shortest path is blocked. If not, report that some shortest path is unblocked. These searches could be done with a simple DFS, or since you're running multiple searches and just want to know if they're connected, using a strongly connected components algorithm once and checking if any pair of important nodes are in the same SCC. Either approach takes time O(n).
Thus the time to solve this problem is at most O(n2), with the bottleneck being the time required to construct the graph.
Hope this helps!
Here's my idea:
I'll describe the case when the destination is upper and to right from the source, for other cases, rotate. (For simple cases where the nodes have the same x/y coordinate, just checks whether there's a blocking node directly between them)
Take the matrix with source and destination in corners. Now, a column at a time, from left to right and inside the column, bottom up, mark blocked nodes. A node B is blocked iff any of following is true:
B is an intermediate node
the nodes left to B and bottom from B are both blocked (both were already checked given the order of processing) or outside the bounds of the matrix
In the end, if destination is blocked, there's no free shortest path.
The time required is O(m*n), where m, n are the lengths of sides of the matrix. So when you'll only have several intermediate nodes, templatetypedef's solution may be more appropriate.
EDIT: Got it a little wrong previously, now I hope I didn't miss anything