I'm looking for an algorithm that seems very typical to me, but it seems that the common solutions are all just a little bit different.
In an undirected graph, I want the shortest path that visits every node. Nodes can be revisited and I do not have to return to the start node.
The Travelling Salesman Problem seems to add the restriction that each node can only be visited once and that the path has to return to where it started.
Minimal Spanning Trees may be part of a solution, but such algorithms only provide the tree, not a minimal path. Additionally, because they're trees and therefore have no loops, they force backtracking where a loop may be more efficient.
You can reduce it to the normal Travelling Salesman Problem by transforming the graph.
First, compute the minimum distance for every pair of nodes. You can use Floyd-Warshall algorithm for that. Once you have it, just construct the complete graph where the edge between nodes u and v is the minimum cost from u to v.
Then, you can apply a normal TSP algorithm as you don't have to revisit nodes anymore, that's already hidden in the costs of the edges.
We can use a modified BFS.
Basically from any node during a BFS we need to be able to traverse nodes already travelled but how do we make sure we're not forming infinite cycles.
We store visited state for "ALL" nodes from each node, what this means is if we've walked over node 1 and we need to traverse back over it we can as long as our total state of "ALL" nodes has not been seen before. This is the reason for the bitmask and not a simple Set of Integers. Note you can use a Set of Strings to store the state as well it just runs a slower.
public int shortestPathInSmallGraph(int[][] graph) {
if (graph.length == 1) {
return 0;
}
Set<Integer>[] adj = new HashSet[graph.length];
int n = graph.length;
int endState = (1 << n) - 1;
boolean[][] seen = new boolean[n][endState];
Queue<int[]> queue = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
queue.add(new int[] {i, 1 << i});
seen[i][1 << i] = true;
}
int steps = 0;
while (!queue.isEmpty()) {
int count = queue.size();
for (int i = 0; i < count; i++) {
int[] pair = queue.poll();
int node = pair[0];
int state = pair[1];
for (int neighbor : graph[node]) {
int nextState = state | (1 << neighbor);
if (nextState == endState) {
return 1 + steps;
}
if (!seen[neighbor][nextState]) {
seen[neighbor][nextState] = true;
queue.add(new int[] {neighbor, nextState});
}
}
}
steps++;
}
return -1;
}
Related
I am learning weighted quick union algorithm by princeton university. I am stuck on mathematical proof of how this algorithm has time complexity of O(lgN)? I know how it is better than normal quick union merging smaller trees below larger tress but how did they come out with the time complexity lgN is something which I couldn't understand. It would be helpful if you can help me understand this. This is the code I am working on:
public class WeightedQuickUnionUF {
private int[] id;
private int[] sz; //an extra array `enter code here`to count the number of objects in the tree rooted at i.
public WeightedQuickUnionUF(int N){
sz = new int[N];
id = new int[N];
for(int i = 0 ; i<N ; i++){
id[i] = i;
sz[i] = 1;
}
}
public int root(int i){
while(id[i] != i)
i = id[i];
return i;
}//will keep searching until the index and the value at the index are equal
public boolean connected(int p , int q){
return root(p) == root(q);
}//identical to quick union
public void union(int p , int q){
int i = root(p);
int j = root(q);
if(i == j )
return;
if(sz[i] < sz[j]){
id[i] = j;
sz[j] += sz[i];
}
else{
id[j]= i;
sz[i] += sz[j];
}
System.out.println(p + " and " + q + " are now connected.");
}/*Link smaller tree to root of larger tree
and update the sz[]array */
public void print(){
System.out.println("0 1 2 3 4 5 6 7 8 9");
System.out.println("-------------------");
for (int j : id) {
System.out.print(j + " ");
}
}
}
If you attach the smaller tree to the root of the larger, the total height only increases (by one) if the two heights are the same.
So a strategy to obtain a tree as high as possible with as few nodes as possible (which corresponds to the worst case) is by making trees of equal height only and uniting them.
The picture below shows trees of heights from 1 to 4 with the minimum number of nodes. It is obvious that they include n=2^(h-1) nodes.
It is important to realize that using the "weighted" rule, it is impossible to build higher trees with the same number of nodes, hence h=1+lg(n) is the worst case. (And of course, the running time for a find is proportional to the tree height in the worst case.)
Here is an algo coding question:
There are stones in two dimension array, and given a known length rope, determine what's the maximum number of stones you can use the rope to surround? (the rope must be enclosed).
I think the question is to find the loop in undirected graph with maximal number of nodes and upper bound of length and BFS seems a good way, but seems still NP-hard.
EDIT:
Here is an example: 5 stones in a plane. I listed three surroundings: black, red and blue. The length of each surrounding is the sum of its strings.
The question is equivalent to select n of N points by order x1,...xn with its sum = d(x1,x2)+d(x2,x3)+...+d(xn-1,xn)+d(xn,x1) <= C. Here n>1. What's the maximal n?
Solution:
After some hints, I think it is nothing about the undirected graph. It is similar to the problem that selecting some elements from a given array equal to a target sum. But the difference is that the result depends on the selecting order, therefore we cannot select by the order from the given array. Here is my solution. But there are still a lot of duplicate searches, for example, if black loop is eligible, we have to search 4 times from each of its nodes.
double d(const vector<double> a, const vector<double> b)
{
return sqrt((a[0] - b[0])*(a[0] - b[0]) + (a[1] - b[1])*(a[1] - b[1]));
}
//recursive
//out: current legal elements; sum: the sum of out; ct: the maximal number up to now
//visited: record the elements in out has been visited to void duplicate visit
//res: help to check the result in debuge
void help(vector<vector<double>> array, double target, vector<vector<double>> &out, int &sum, int & ct, vector<vector<vector<double>>> &res, vector<bool> &visited)
{
for (int i = 0; i < array.size(); i++) //since it depends on the order of selection, we have to search from begining. visited helps us to skip the element already been in out.
{
if (visited[i]) continue;
else if (out.empty()) {out.push_back(array[i]); visited[i] = true;}
else
{
vector<double> last = out.back(), first = out.front();
if (sum + d(array[i], last) + d(array[i], first) <= target)
{
out.push_back(array[i]);
sum += d(array[i], last);
ct = max(ct, (int)out.size());
visited[i] = true;
help(array, target - d(array[i], last), out, sum, ct, res, visited);
out.pop_back();//when the loop is over at this level, we should return to the state of previous level
sum -= d(array[i], last);
visited[i] = false;
}
else
{
res.push_back(out);
return;
}
}
}
}
int Path(vector<vector<double>> array, double target) {
int sum = 0, ct = 0;
vector<vector<double>> out;
vector<vector<vector<double>>> res;
vector<bool> visited(array.size(), false);
help(array, target, out, sum, ct, res, visited);
return ct;
}
In my algorithms and datastructures class I have been asked to implement the Manhattan tourist problem using dynamic programming.
I have come to a solution using a combination of dynamic programming and recursive calls, but I seem to get "Time limit exceeded" when putting it to the test on CodeJudge. I haven't been able to figure out why my code isn't fast enough. Any takers?
Best regards.
Description of the problem:
Your are helping the tourist guide company "Manhattan Tourists", that are arranging
guided tours of the city. They want to find a walk between two points on the map that is both interesting and short. The map is a square grid graph. The square grid graph has n rows with n nodes in each row. Let node vi,j denote the jth node on row i. For 1≤I<n and for 1≤j≤n node vi,j is connected to vi+1, j. And for 1≤i≤n and for 1 ≤ j < n node vi,j is connected to vi,j+1. The edges have non-negative edge weights that indicate how interesting that street is. See the graph below for an example of a 5 × 5 grid graph.
They want to find a short interesting walk from the upper left corner (s = v1,1) to the lower right corner (t = vn,n). More precisely, they want to find a path with the possible smallest number of edges, and among all paths with this number of edges they want the path with the maximum weight (the weight of a path is the sum of weights on the path).
All shortest paths have 2n − 2 edges and go from s to t by walking either down or right in each step. In the example below two possible shortest paths (of length 8) are indicated. The dashed path has weigth 38 and the dotted path has weight 30.
Let W [i, j] be the maximal weight you can get when walking from s to vi, j walking either down or right in each step. Let D[i, j] be the weight of the edge going down from vi, j and let R[i, j] be the weight of the edge going right from vi,j.
Description on CodeJudge:
Exercise
Before you can solve this exercise, you must first read, understand and (partly) solve the problem Manhattan Tourists described on the weekplan.
Your task here is to implement your solution. Read the input/output specification below and look at the sample test data in order to learn how to read the input and write the output.
Input format
Line 1: The integer n (1<= n <= 1000).
Line 2..n+1: the n rows of R, each consisting of n-1 integers separated by space.
Line n+2..2n: the n-1 rows of D, each consisting of n integers separated by space.
Output format:
Line 1: The maximum interest score of a shortest walk.
Heres my code so far:
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
int n = console.nextInt();
int[][] R = new int[n][n-1];
int[][] D = new int[n-1][n];
for(int i = 0; i < n; i++) {
for(int j = 0; j < n-1; j++) {
R[i][j] = console.nextInt();
}
}
for(int i = 0; i < n-1; i++) {
for(int j = 0; j < n; j++) {
D[i][j] = console.nextInt();
}
}
System.out.println(opt(R, D, n, n-1, n-1));
}
public static int opt(int[][]R, int[][]D, int n, int i, int j) {
int[][] result = new int[n][n];
if(i==0 && j==0) {
if(result[i][j] == 0) {
result[i][j] = 0;
}
return result[i][j];
} else if(i == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i,j-1) + R[i][j-1];
}
return result[i][j];
}else if(j == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i-1,j) + D[i-1][j];
}
return result[i][j];
}else if(result[i][j] == 0) {
result[i][j] = max(opt(R, D, n, i, j-1) + R[i][j-1],opt(R, D, n, i-1, j) + D[i-1][j]);
}
return result[i][j];
}
public static int max(int i, int j) {
if(i > j) {
return i;
}
return j;
}
}
Why a recursion?
The topmost row can be traversed horizontally only. So, for each vertex in the first row the total weight is a sum of weights of branches to the left. You can compute all of them in a single loop as a running total across the row.
For each next row the total weight of the first vertex is a weight of the vertex above it plus the weight of the branch between them. And the total weight of each next vertex in the row is the bigger one from two possible when coming from above or from left.
All that can be computed iteratively with two nested loops.
I am working on programming where I need to find the articulation points of a graph (nodes such that removing any of them makes the graph disconnected)
For example, I have these links:
Example 1
[[0,1], [0,2], [1,3], [2,3], [5,6], [3,4]]
The answer should be [2,3,5], because removing these nodes makes the graph disconnected.
Explanation:
If I remove node 2 here, the graph becomes 2 parts 0,1,3,4 and 5,6
If I remove node 3 here, the graph becomes 2 parts 0,1,2,5,6 and 4
If I remove node 5 here, the graph becomes 2 parts 0,1,2,3,4 and 6
Example 2:
[[1,2], [2,3], [3,4], [4,5], [6,3]]
The output should be: [2, 3, 4]
Explanation:
If I remove node 2 here, the graph becomes 2 parts 1, and 3,4,5,6
If I remove node 3 here, the graph becomes 3 parts 1,2 and 6 and 4,5
If I remove node 4 here, the graph becomes 2 parts 1,2,3,6 and 5
How to achieve this in a Java program?
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.List;
public class ArticulationPointsAdjacencyList {
private int n, id, rootNodeOutcomingEdgeCount;
private boolean solved;
private int[] low, ids;
private boolean[] visited, isArticulationPoint;
private List<List<Integer>> graph;
public ArticulationPointsAdjacencyList(List<List<Integer>> graph, int n) {
if (graph == null || n <= 0 || graph.size() != n) throw new IllegalArgumentException();
this.graph = graph;
this.n = n;
}
// Returns the indexes for all articulation points in the graph even if the
// graph is not fully connected.
public boolean[] findArticulationPoints() {
if (solved) return isArticulationPoint;
id = 0;
low = new int[n]; // Low link values
ids = new int[n]; // Nodes ids
visited = new boolean[n];
isArticulationPoint = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
rootNodeOutcomingEdgeCount = 0;
dfs(i, i, -1);
isArticulationPoint[i] = (rootNodeOutcomingEdgeCount > 1);
}
}
solved = true;
return isArticulationPoint;
}
private void dfs(int root, int at, int parent) {
if (parent == root) rootNodeOutcomingEdgeCount++;
visited[at] = true;
low[at] = ids[at] = id++;
List<Integer> edges = graph.get(at);
for (Integer to : edges) {
if (to == parent) continue;
if (!visited[to]) {
dfs(root, to, at);
low[at] = min(low[at], low[to]);
if (ids[at] <= low[to]) {
isArticulationPoint[at] = true;
}
} else {
low[at] = min(low[at], ids[to]);
}
}
}
/* Graph helpers */
// Initialize a graph with 'n' nodes.
public static List<List<Integer>> createGraph(int n) {
List<List<Integer>> graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
return graph;
}
// Add an undirected edge to a graph.
public static void addEdge(List<List<Integer>> graph, int from, int to) {
graph.get(from).add(to);
graph.get(to).add(from);
}
/* Example usage: */
public static void main(String[] args) {
testExample2();
}
private static void testExample1() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 0, 1);
addEdge (graph, 0, 2);
addEdge (graph, 1, 3);
addEdge (graph, 2, 3);
addEdge (graph, 2, 5);
addEdge (graph, 5, 6);
addEdge (graph, 3, 4);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 5 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
private static void testExample2() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 1, 2);
addEdge (graph, 2, 3);
addEdge (graph, 3, 4);
addEdge (graph, 3, 6);
addEdge (graph, 4, 5);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 4 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
}
Reference: https://github.com/williamfiset/Algorithms/blob/master/com/williamfiset/algorithms/graphtheory/ArticulationPointsAdjacencyList.java
There are different algorithms used to find the nodes such that if removed they make the graph disconnected (called articulation points).
Here I explain one of them and I provide some code that implements it:
Tarjan Algorithm
Given a graph we want to find all the such that if is removed from the graph become disconnected
The first observation is that the a (weak) connected component in a directed graph is equal to a connected component in the same graph, but where the edges are undirected. So for simplicity we consider as an undirected graph.
Algorithm description
On the graph we run a pre-order Depth First Search (DFS) visit where for any node we assign 2 values, let's call it pre and low. pre represent the instant when the node is visited and low the instant of the lowest reachable node from .
The visit works in this way:
At every step of the visit both pre and low of are set to the next value of pre. Then if we find that a cycle is being closed we set low to pre of the start cycle node. low value is transmitted to parent through DFS backtracking.
When the DFS finish for every couple of nodes such that and are neighbor and low value of is greater or equal to the pre value of then is an articulation point.
For this there is an exception: the root of the DFS spanning tree is an articulation point only if it has more than 1 children
Example
(In the graph P obviously means pre and L means low)
At first pre and low of every vertex are set to a default value (let's say -1)
We start from node 0 and set his pre and low
We go to node 1 and set his pre and low
We can go to 2 or 3, we decide to go to 2 and set his pre and low
We can go to 4 or 5, we decide to go to 4 and set his pre and low
We go to 3 and set his pre and low
We see that 1 is alredy visited; that means it is a cycle, so we update low of 3 to pre of 1
Through backtrack we return to 4 and update his low value
Through backtrack we return to 2 and update his low value
Now we go to 5 and set his pre and low
Through backtrack we return to 2, but there's nothing to do.
We returned from 5 so his low value is fixed and is greater than pre value of 2; so 2 is an articulation point
Through backtrack we return to 1, and there's nothing to do.
We returned from 2 so his low value is fixed and is equal to the pre value of 1; so 1 is an articulation point
Through backtrack we return to 0, but there's nothing to do.
We returned from 1 so his low value is fixed and is greater than pre value of 0; but 0 is the root and has only one child; so it isn't an articulation point
So we have found the answer: [1, 2]
Code
Here is a simple really easy to understand snippet of code (C++) extracted from Competitive Programming Handbook by S. Halim and F. Halim and modified by me.
It is not very adapt to "real word application" (for example because it uses global variables) but it is ok for competitive programming and explaining due to his brevity and clearness.
const int UNVISITED = -1;
vector<int> dfs_low;
vector<int> dfs_pre;
int dfsNumberCounter;
int rootChildren;
vector<vector<int>> AdjList;
vector<int> articulation_vertex;
// This function is the DFS that implement Tarjan algoritm
void articulationPoint(int u) {
dfs_low[u] = dfs_pre[u] = dfsNumberCounter++; // dfs_low[u] <= dfs_pre[u]
for (int j = 0; j < (int)AdjList[u].size(); j++) {
int v = AdjList[u][j];
if (dfs_pre[v] == UNVISITED) { // a tree edge
dfs_parent[v] = u;
if (u == dfsRoot) rootChildren++; // special case if u is a root
articulationPoint(v);
if (dfs_low[v] >= dfs_pre[u]) // for articulation point
articulation_vertex[u] = true; // store this information first
dfs_low[u] = min(dfs_low[u], dfs_low[v]); // update dfs_low[u]
}
else if (v != dfs_parent[u]) // a back edge and not direct cycle
dfs_low[u] = min(dfs_low[u], dfs_pre[v]); // update dfs_low[u]
} }
// Some driver code
int main() {
... //Init of variables and store of the graph inside AdjList is omitted
... // V is the number of nodes
dfsNumberCounter = 0;
dfs_pre.assign(V, UNVISITED);
dfs_low.assign(V, 0);
dfs_parent.assign(V, 0);
articulation_vertex.assign(V, 0);
rootChildren = 0;
articulationPoint(0);
if (root_children > 1) {
articulation_vertex[0] = false;
}
printf("Articulation Points:\n");
for (int i = 0; i < V; i++)
if (articulation_vertex[i])
printf(" Vertex %d\n", i);
}
This question already has answers here:
Bridges in a connected graph
(4 answers)
Closed 7 years ago.
Given an undirected Graph, how can I find all the bridges? I've only found Tarjan's algorithm which seems rather complicated.
It seems there should be multiple linear time solutions, but I can't find anything.
Tarjan's algorithm was the first bridge finding algorithm in an undirected graph that ran in linear time. However a simpler algorithm exists and you can have a look at its implementation here.
private int bridges; // number of bridges
private int cnt; // counter
private int[] pre; // pre[v] = order in which dfs examines v
private int[] low; // low[v] = lowest preorder of any vertex connected to v
public Bridge(Graph G) {
low = new int[G.V()];
pre = new int[G.V()];
for (int v = 0; v < G.V(); v++) low[v] = -1;
for (int v = 0; v < G.V(); v++) pre[v] = -1;
for (int v = 0; v < G.V(); v++)
if (pre[v] == -1)
dfs(G, v, v);
}
public int components() { return bridges + 1; }
private void dfs(Graph G, int u, int v) {
pre[v] = cnt++;
low[v] = pre[v];
for (int w : G.adj(v)) {
if (pre[w] == -1) {
dfs(G, v, w);
low[v] = Math.min(low[v], low[w]);
if (low[w] == pre[w]) {
StdOut.println(v + "-" + w + " is a bridge");
bridges++;
}
}
// update low number - ignore reverse of edge leading to v
else if (w != u)
low[v] = Math.min(low[v], pre[w]);
}
}
The algorithm does the job by maintaining 2 arrays pre and low. pre holds the pre-order traversal numbering for the nodes. So pre[0] = 2 means that vertex 0 was discovered in the 3rd dfs call. And low[u] holds the smallest pre-order number of any vertex that is reachable from u.
The algorithm detects a bridge whenever for an edge u--v, where u comes first in the preorder numbering, low[v]==pre[v]. This is because if we remove the edge between u--v, v can't reach any vertex that comes before u. Hence removing the edge would split the graph into 2 separate graphs.
For a more elaborate explanation you can also have a look at this answer .