Given a Graph with N nodes. Two players A and B start from node 1 and node N respectively. A can visit all the adjacent nodes to the nodes already visited by A but can not visit any nodes which are already visited by B and similarly for B also. Suppose A moves first. Find the winner and maximum nodes visited by the winner.
I know a solution for the tree using DFS, but for Graph, I am not able to construct a solution.
Related
Say we have a DAG comprised of a list of nodes A, B, C, D, and E.
Each node has a list of reachable nodes - for example:
A --> B, C
A --> B
D --> E
In this case, we would have to visit nodes A and D to comprehensively visit all nodes in the graph. What is the best algorithm to approach this problem in general?
Here is a linear approach:
For every node count it`s in-degree (number of edges pointing to it)
Because graph is a DAG (no cycles) we can just take all nodes with in-degree of 0 as our starting sub-set
Time Complexity(N + M) - linear in graph size
Here is an approach.
Let's say that node A is parent of node B if there is an arc from A to B.
And node C is the most-parent of node B, if it has no parent and there is a path from C to B.
Mark every node as not visited.
For every node in DAG you define it's parent.
for every node A that is not visited
Find A's most-parent MP
Mark all nodes that are reachable from MP as visited
Put MP to array
After this you'll get smallest subset of nodes that reach all nodes in DAG in array
Time compexity of algo is O(n^2)
Given an unoriented tree with weightless edges with N vertices and N-1 edges and a number K find K nodes so that every node from a tree is within S distance of at least one of the K nodes. Also, S has to be the smallest possible S, so that if there were S' < S at least one node would be unreachable in S' steps.
I tried solving this problem, however, I feel that my supposed solution is not very fast.
My solution:
set x=1
find nodes which are x distance from every node
let the node which has the most nodes in its distance be one of the K nodes.
recompute for every node whilst not counting already covered nodes.
do this till I find K number of K nodes. Then if every node is covered we are done else increase x.
This problem is called p-center, and you can find several papers online about it such as this. It is indeed NP for general graphs, but polynomial on trees, both weighted and unweighted.
For me it looks like a clustering problem. Try it with the k-Means (wikipedia) algorithm where k equals to your K. Since you have a tree and all vertices are connected, you can use as distance measurement the distance/number of edges between your vertices.
When the algorithm converts you get the K nodes which should be found. Then you can determine S by iterating through all k clusters. There you calculate the maximum distance for every node in the cluster to the center node. And the overall max should be S.
Update: But actually I see that the k-means algorithm does not produce a global optimum, so this algorithm wouldn't also produce the best result ...
You say N nodes and N-1 vertices so your graph is a tree. You are actually looking for a connected K-subset of nodes minimizing the longest edge.
A polynomial algorithm may be:
Sort all your edges increasing distance.
Then loop on edges:
if none of the 2 nodes are in a group, create a new group.
else if one node is in 1 existing goup, add the other to the group
else both nodes are in 2 different groups, then fuse the groups
When a group reach K, break the loop and you have your connected K-subset.
Nevertheless, you have to note that your group can contain more than K nodes. You can imagine the problem of having 4 nodes, closed two by two. There would be no exact 3-subset solution of your problem.
I have some interesting question that I would like to get your help:
Let's say I have a graph (Data structure) with n(n-1)/2 edges, which means, completed graph.
How many possible different DFS trees can I get from one DFS scan from some random element in the graph?
Your question is interesting. I believe you are talking about complete graph with n vertices and n(n-1)/2 edges in between them.
If we begin depth first search (DFS) from any vertex, it will end up visiting exactly n vertices. In DFS, we keep track of visited vertices so that we will not visit them once they are visited and hence outgoing option reduces as long as DFS progresses. We can summarize this as:
There are total n options to choose first vertex.
There are total n-1 options to choose second vertex as 1 vertex is already visited.
There are total n-2 options to choose third vertex as 2 vertices are already visited.
There are total n-3 options to choose third vertex as 3 vertices are already visited.
And so on . . .
There is only 1 option to choose nth vertex.
Hence, different possible DFS trees that we can get from DFS in such graph is :
Total ways = n*(n-1)*(n-2)*(n-3)*....*1
= n! ( n factorial )
In fact it's not a tree but a list of nodes of the given complete graph. Thus, the question is: How many permutations of n nodes of the graph? Apparently, the answer is n!.
I have searched the net and could not find any explanation of a DFS algorithm for finding all articulation vertices of a graph. There is not even a wiki page.
From reading around, I got to know the basic facts from here. PDF
There is a variable at each node which is actually looking at back edges and finding the closest and upmost node towards the root node. After processing all edges it would be found.
But I do not understand how to find this down & up variable at each node during the execution of DFS. What is this variable doing exactly?
Please explain the algorithm.
Thanks.
Finding articulation vertices is an application of DFS.
In a nutshell,
Apply DFS on a graph. Get the DFS tree.
A node which is visited earlier is a "parent" of those nodes which are reached by it and visited later.
If any child of a node does not have a path to any of the ancestors of its parent, it means that removing this node would make this child disjoint from the graph.
There is an exception: the root of the tree. If it has more than one child, then it is an articulation point, otherwise not.
Point 3 essentially means that this node is an articulation point.
Now for a child, this path to the ancestors of the node would be through a back-edge from it or from any of its children.
All this is explained beautifully in this PDF.
I'll try to develop an intuitive understanding on how this algorithm works and also give commented pseudocode that outputs Bi-Components as well as bridges.
It's actually easy to develop a brute force algorithm for articulation points. Just take out a vertex, and run BFS or DFS on a graph. If it remains connected, then the vertex is not an articulation point, otherwise it is. This will run in O(V(E+V)) = O(EV) time. The challenge is how to do this in linear time (i.e. O(E+V)).
Articulation points connect two (or more) subgraphs. This means there are no edges from one subgraph to another. So imagine you are within one of these subgraphs and visiting its node. As you visit the node, you flag it and then move on to the next unflagged node using some available edge. While you are doing this, how do you know you are within still same subgraph? The insight here is that if you are within the same subgraph, you will eventually see a flagged node through an edge while visiting an unflagged node. This is called a back edge and indicates that you have a cycle. As soon as you find a back edge, you can be confident that all the nodes through that flagged node to the one you are visiting right now are all part of the same subgraph and there are no articulation points in between. If you didn't see any back edges then all the nodes you visited so far are all articulation points.
So we need an algorithm that visits vertices and marks all points between the target of back edges as currently-being-visited nodes as within the same subgraph. There may obviously be subgraphs within subgraphs so we need to select largest subgraph we have so far. These subgraphs are called Bi-Components. We can implement this algorithm by assigning each bi-component an ID which is initialized as just a count of the number of vertices we have visited so far. Later as we find back edges, we can reset the bi-compinent ID to lowest we have found so far.
We obviously need two passes. In the first pass, we want to figure out which vertex we can see from each vertex through back edges, if any. In the second pass we want to visit vertices in the opposite direction and collect the minimum bi-component ID (i.e. earliest ancestor accessible from any descendants). DFS naturally fits here. In DFS we go down first and then come back up so both of the above passes can be done in a single DFS traversal.
Now without further ado, here's the pseudocode:
time = 0
visited[i] = false for all i
GetArticulationPoints(u)
visited[u] = true
u.st = time++
u.low = v.st //keeps track of highest ancestor reachable from any descendants
dfsChild = 0 //needed because if no child then removing this node doesn't decompose graph
for each ni in adj[i]
if not visited[ni]
GetArticulationPoints(ni)
++dfsChild
parents[ni] = u
u.low = Min(u.low, ni.low) //while coming back up, get the lowest reachable ancestor from descendants
else if ni <> parent[u] //while going down, note down the back edges
u.low = Min(u.low, ni.st)
//For dfs root node, we can't mark it as articulation point because
//disconnecting it may not decompose graph. So we have extra check just for root node.
if (u.low = u.st and dfsChild > 0 and parent[u] != null) or (parent[u] = null and dfsChild > 1)
Output u as articulation point
Output edges of u with v.low >= u.low as bridges
output u.low as bicomponent ID
One fact that seems to be left out of all the explanations:
Fact #1: In a depth first search spanning tree (DFSST), every backedge connects a vertex to one of its ancestors.
This is essential for the algorithm to work, it is why an arbitrary spanning tree won't work for the algorithm. It is also the reason why the root is an articulation point iff it has more than 1 child: there cannot be a backedge between the subtrees rooted at the children of the spanning tree's root.
A proof of the statement is, let (u, v) be a backedge where u is not an ancestor of v, and (WLOG) u is visited before v in the DFS. Let p be the deepest ancestor of both u and v. Then the DFS would have to visit p, then u, then somehow revisit p again before visiting v. But it isn't possible to revisit p before visiting v because there is an edge between u and v.
Call V(c) the set of vertices in the subtree rooted at c in the DFSST
Call N(c) the set of vertices for which that have a neighbor in V(c) (by edge or by backedge)
Fact #2:
For a non root node u,
If u has a child c such that N(c) ⊆ V(c) ∪ {u} then u is an articulation point.
Reason: for every vertex w in V(c), every path from the root to w must contain u. If not, such a path would have to contain a back edge that connects an ancestor of u to a descendant of u due to Fact #1, making N(c) larger than V(c).
Fact #3:
The converse of fact #2 is also true.
Reason: Every descendant of u has a path to the root that doesn't pass through u.
A descendant in V(c) can bypass u with a path through a backedge that connects V(c) to N(c)/V(c).
So for the algorithm, you only need to know 2 things about each non-root vertex u:
The depth of the vertex, say D(u)
The minimum depth of N(u), also called the lowpoint, lets say L(u)
So if a vertex u has a child c, and L(c) is less than D(u), then that mean the subtree rooted at c has a backedge that reaches out to an ancestor of u which makes it not an articulation point by Fact #3. Conversely also by Fact #2.
If low of the descendant of u is greater than the dfsnum of u, then u is said to be the Articulation Point.
int adjMatrix[256][256];
int low[256], num=0, dfsnum[256];
void cutvertex(int u){
low[u]=dfsnum[u]=num++;
for (int v = 0; v < 256; ++v)
{
if(adjMatrix[u][v] && dfsnum[v]==-1)
{
cutvertex(v);
if(low[v]>dfsnum[u])
cout<<"Cut Vertex: "<<u<<"\n";
low[u]=min(low[u], low[v]);
}
else{
low[u]=min(low[u], dfsnum[v]);
}
}
}
I am given a connected graph with N nodes (numbered from 1..N) and M bidirectional edges consisting in a couple (A,B). Edges are unweighted.
I have K people starting at node 1 and I want to explore every node of the graph. I takes one unit of time to a person to travel from one node to one of its neighbor.
How long will it take to explore every node? I am searching for an efficient algorithm to compute the minimum traversal time, but I am afraid it is an NP-complete problem. (The constraints on the number of edges and number of people are small though).
Suppose K were 1. Then the minimisation problem reduces to finding a minimum length path that touches every node at least once.
If we construct a new weighted graph G' with the same nodes and with edges between every two nodes whose weight is the minimum distance between those nodes in the original graph, then the minimum length path through all the nodes in G is the minimum length Hamiltonian path through G', the travelling salesperson problem, which is well-known to be NP-complete.
So for at least one value of K, the problem is NP-complete. However, for large values of K (say, ≥ N), we can produce a minimum solution in much less time, since we can just construt the minimum spanning tree and find the distance of the furthest element. I doubt whether there is any such simplified solution for small values of K, but I'd definitely use the MST as a heuristic for finding a reasonable solution.
To me this seems like BFS.
You can view the graph like a tree, where the starting node is the root. From this perspective, the answer would be the deepest leaf(farthest away from the root) if the number of leafs <= the number of people, with the answer being the depth of that leaf.
This is correct because if each person visits every leaf, then in the process of doing so visits all the nodes are visited.
However, if there are still nodes left unvisited after every person visits a leaf, then you have add to the answer the max time it takes(or distance) for the closest person to visit the unvisited node.
This is not the complete answer, however. It's more complicated than that.
If you have the following image:
You wouldn't want to blindly bfs. You would want to visit the nodes in order from least deep to deepest, as that way you don't have to go down and up the path again. For example, 0 -> 1 -> 0 -> 2 -> 0 -> 3 -> 0 -> 4 is more efficient than 0 -> 4 -> 0 -> 3 -> 0 -> 2 -> 0 -> 1. The reason this is correct is because you can only save time on your last traversal, so you want to make that one the longest.
Furthermore, perhaps getting a person from a different branch to visit to unvisited node may be more efficient(to help the person from the current branch), so you want to assign unvisited nodes to the people of the surrounding branches if the time it takes for that person to get to 0 is less than the time it takes for the person(s) in the current branch to visit all the nodes in this branch. If one person from a branch can be assigned to multiple other branches, you want to take the branch that has the greatest number of unvisited nodes. This "helper" person(s) is also why you want to visit the nodes from least deep to deepest in order instead of just visiting the deepest node last.
All that might sound confusing, but the key is BFS. That's the solution to your problem. It's basically BFS with modifications.
As to implementation, you can use recursions(or stacks), which are usually used for tree traversals. And note that for the case that unvisited nodes > num of people, you don't have to simulate the traveling to the remaining unvisited nodes.