I've been struggling a lot to make sense out of this graph presentation without any proper solution. Maybe someone could figure something out.
I have a presentation of connected, cycle free graph that forms as follows:
Remove vertices which has a degree of 1 (has only one edge) one by one
If there is more than one option, vertex with the lowest value will be removed
When vertex is removed, the vertex next to it will me marked
This will go on until graph has only one vertex left
Heres an example graph:
2 3
\ /
5 1
\ /
4
And this is how the presentation forms:
2 3 3
\ / /
5 1 => 5 1 => 5 1 => 5 => 5
\ / \ / \ / \
4 4 4 4
1. Remove vertex two and mark one.
2. Remove vertex three and mark one.
3. Remove vertex one and mark four.
4. Remove vertex four and mark five.
So the presentation for this graph would be:
1 1 4 5
The problem is, how can I turn this presentation into adjacency matrix or adjacency list?
F.e. with 1 1 4 5, the adjacency list would look like this:
1: 2 3 4
2: 1
3: 1
4: 1 5
5: 4
Thank you!
Ah! because of the insufficient info in the original question (especially the info: tree will have 1 to n+1 nodes, where n is the length of input array), I tried to solve it in much harder way! Anyway, here is my Prufer-tree generation implementation, maybe it will help :-? :
#include <stdio.h>
#include <vector>
#include <memory.h>
using namespace std;
struct Node {
int N;
vector<int>list;
Node() {
N=-1;
list.clear();
}
};
vector<Node> convertPruferToTree(vector<int>& input) {
int n = input.size()+1;
vector<Node> T;
int *degree = new int[n+1];
for (int i=1; i<=n; i++) {
Node tmp;
tmp.N = i;
T.push_back(tmp);
degree[i]=1;
}
//printf("n: %d\n", n);
for (int i=0; i<input.size()-1; i++) {
degree[input[i]]++;
}
for (int i=0; i<input.size()-1; i++) {
for (int j=1; j<=n; j++) {
if (degree[j]==1) {
T[j-1].list.push_back(input[i]);
T[input[i]-1].list.push_back(j);
degree[input[i]]--;
degree[j]--;
break;
}
}
}
int u=0, v=0;
for (int i=1; i<=n; i++) {
if (degree[i]==1) {
if (u==0) u=i;
else {
v = i;
break;
}
}
}
//printf("u: %d v: %d\n", u, v);
T[u-1].list.push_back(v);
T[v-1].list.push_back(u);
delete []degree;
return T;
}
int main () {
vector <int> input;
int n,v;
scanf("%d", &n);
while(n--) {
scanf("%d", &v);
input.push_back(v);
}
vector<Node> adjList = convertPruferToTree(input);
Node tmp;
for (int i=0; i<adjList.size(); i++) {
tmp = adjList[i];
printf("%2d: ", tmp.N);
for (int j=0; j<tmp.list.size(); j++) {
printf("%2d ", tmp.list[j]);
}
printf("\n");
}
return 0;
}
Here is the naive implementation in python:
from collections import defaultdict
prufer_sequence = [1, 1, 4, 5]
all_vertices = range(1, len(prufer_sequence) + 2)
adjacency = defaultdict(list)
for vertex in prufer_sequence:
searched_vertex = filter(lambda v: v != vertex, all_vertices)[0]
all_vertices.remove(searched_vertex)
adjacency[vertex].append(searched_vertex)
adjacency[searched_vertex].append(vertex)
print adjacency
And output:
defaultdict(<type 'list'>, {1: [2, 3, 4], 2: [1], 3: [1], 4: [1, 5], 5: [4]})
The "presentation" (1 1 4 5 in your example) can be turned back into a graph using the following technique (which, from your comment above, is the bit I think you're struggling with). You can then trivially produce an adjacency matrix/list.
This technique relies on the key assumption that the nodes in the graph were labelled 1 - N (where there are N nodes in the graph). If this is not the case, it is fundamentally impossible to reconstruct the original graph because you can never determine the identity of the first removed node.
Note that there are 4 items in the presentation. Therefore, there are 5 nodes in the graph.
Work backwards from the end of the presentation.
When the last node was removed, the node that was left was 5. Therefore, the graph looks like...
5 - ?
When the previous item was removed, 4 was marked. Therefore, the original question-mark must actually be node 4, and we have a new unknown node.
5 - 4 - ?
When the previous item was removed, 1 was marked. Therefore, the ? must be a 1 and there's a new unknown node.
5 - 4 - 1 - ?A
Finally, when the previous item was removed, 1 was marked. We already have a node 1, so we must attach to that.
5 - 4 - 1 +- ?A
|
+= ?B
We've finished parsing the input. Now we just need to label the outstanding ?s. We know that the values are 2 and 3 because of the assumption stated above that the nodes are labelled 1 - N and we already have 1, 2 & 5. Because lowest value nodes are removed first (when turning the graph into the presentation), they are added last when converting a presentation to a graph. So ?A = 3 and ?B = 2. (In this case it doesn't matter, but in the general case it does.) That leaves that final graph as follows.
5 - 4 - 1 +- 3
|
+= 2
...which is good because that's the same as where we started.
From this you can iterate over the nodes and produce your adjacency matrix. Alternatively, so can produce an adjacency list/matrix as you go along (which is likely to be more efficient, but slightly confuses the implementation).
And as David pointed out above, this is very similar (but not quite identical) to a Prüfer sequence which stops when there are 2 nodes left (rather than just 1). The linked article gives an efficient pseudo-code algorithm which can be adapted by skipping the final step (linking together the last two nodes).
I've come up with this algorithm. It's a lot like this http://en.wikipedia.org/wiki/Pr%C3%BCfer_sequence, but like Andrew said I left the last part out of it. The hashmap is for the adjacency list and arraylist k is for the presentation.
public static HashMap<Integer, HashSet<Integer>> toGraph(ArrayList<Integer> k) {
HashMap<Integer, HashSet<Integer>> hm = new HashMap<Integer, HashSet<Integer>>();
for(int i=1; i<=k.size()+1; i++){
hm.put(i, new HashSet<Integer>());
}
int degree[] = new int[k.size()+1];
for(int i=0; i<degree.length; i++){
degree[i]=1;
}
for(int a : k){
degree[a-1]++;
}
for(int n : k){
for(int j : hm.keySet()){
if(degree[j-1]==1){
hm.get(j).add(n);
hm.get(n).add(j);
degree[n-1]--;
degree[j-1]--;
break;
}
}
}
return hm;
}
In some cases one vertex is misplaced in adjacency list returned. F.e. in 16, 1, 19, 9, 19, 18, 17, 10, 13, 13, 4, 19, 5, 19, 18, 4, 19, 19 vertex 3 should have edges to 17, 19, 13 but in mine it has edges to 16, 19, 13. Can someone spot a flaw?
Related
Problem link: UVa 539 - The Settlers of Catan
(UVa website occasionally becomes down. Alternatively, you can read the problem statement pdf here: UVa External 539 - The Settlers of Catan)
This problem gives a small general graph and asks to find the longest road. The longest road is defined as the longest path within the network that doesn’t use an edge twice. Nodes may be visited more than once, though.
Input Constraints:
1. Number of nodes: n (2 <= n <= 25)
2. Number of edges m (1 <= m <= 25)
3. Edges are un-directed.
4. Nodes have degrees of three or less.
5. The network is not necessarily connected.
Input is given in the format:
15 16
0 2
1 2
2 3
3 4
3 5
4 6
5 7
6 8
7 8
7 9
8 10
9 11
10 12
11 12
10 13
12 14
The first two lines gives the number of nodes n and the number of edges m for this test case respectively. The next m lines describe the m edges. Each edge is given by the numbers of the two nodes connected by it. Nodes are numbered from 0 to n - 1.
The above test can be visualized by the following picture:
Now I know that finding the longest path in a general graph is NP-hard. But as the number of nodes and edges in this problem is small and there's a degree bound of each node, a brute force solution (recursive backtracking) will be able to find the longest path in the given time limit (3.0 seconds).
My strategy to solve the problem was the following:
1. Run DFS (Depth First Search) from each node as the graph can be disconnected
2. When a node visits its neighbor, and that neighbor visits its neighbor and so on, mark the edges as used so that no edge can be used twice in the process
3. When the DFS routine starts to come back to the node from where it began, mark the edges as unused in the unrolling process
4. In each step, update the longest path length
My implementation in C++:
#include <iostream>
#include <vector>
// this function adds an edge to adjacency matrix
// we use this function to build the graph
void addEdgeToGraph(std::vector<std::vector<int>> &graph, int a, int b){
graph[a].emplace_back(b);
graph[b].emplace_back(a); // undirected graph
}
// returns true if the edge between a and b has already been used
bool isEdgeUsed(int a, int b, const std::vector<std::vector<char>> &edges){
return edges[a][b] == '1' || edges[b][a] == '1'; // undirected graph, (a,b) and (b,a) are both valid edges
}
// this function incrementally marks edges when "dfs" routine is called recursively
void markEdgeAsUsed(int a, int b, std::vector<std::vector<char>> &edges){
edges[a][b] = '1';
edges[b][a] = '1'; // order doesn't matter, the edge can be taken in any order [(a,b) or (b,a)]
}
// this function removes edge when a node has processed all its neighbors
// this lets us to reuse this edge in the future to find newer (and perhaps longer) paths
void unmarkEdge(int a, int b, std::vector<std::vector<char>> &edges){
edges[a][b] = '0';
edges[b][a] = '0';
}
int dfs(const std::vector<std::vector<int>> &graph, std::vector<std::vector<char>> &edges, int current_node, int current_length = 0){
int pathLength = -1;
for(int i = 0 ; i < graph[current_node].size() ; ++i){
int neighbor = graph[current_node][i];
if(!isEdgeUsed(current_node, neighbor, edges)){
markEdgeAsUsed(current_node, neighbor, edges);
int ret = dfs(graph, edges, neighbor, current_length + 1);
pathLength = std::max(pathLength, ret);
unmarkEdge(current_node, neighbor, edges);
}
}
return std::max(pathLength, current_length);
}
int dfsFull(const std::vector<std::vector<int>> &graph){
int longest_path = -1;
for(int node = 0 ; node < graph.size() ; ++node){
std::vector<std::vector<char>> edges(graph.size(), std::vector<char>(graph.size(), '0'));
int pathLength = dfs(graph, edges, node);
longest_path = std::max(longest_path, pathLength);
}
return longest_path;
}
int main(int argc, char const *argv[])
{
int n,m;
while(std::cin >> n >> m){
if(!n && !m) break;
std::vector<std::vector<int>> graph(n);
for(int i = 0 ; i < m ; ++i){
int a,b;
std::cin >> a >> b;
addEdgeToGraph(graph, a, b);
}
std::cout << dfsFull(graph) << '\n';
}
return 0;
}
I was ordering what is the worst case for this problem? (I'm wondering it should be n = 25 and m = 25) and in the worst case in total how many times the edges will be traversed? For example for the following test case with 3 nodes and 2 edges:
3 2
0 1
1 2
The dfs routine will be called 3 times, and each time 2 edges will be visited. So in total the edges will be visited 2 x 3 = 6 times. Is there any way to find the upper bound of total edge traversal in the worst case?
I've just started with graphs and was printing adjacency list using vector of pairs and unordered_map, though when I test my code against random custom inputs, it matches with the expected results but when I submit it to the online judge it gives me a segmentation fault.
Problem:
Given number of edges 'E' and vertices 'V' of a bidirectional graph. Your task is to build a graph through adjacency list and print the adjacency list for each vertex.
Input:
The first line of input is T denoting the number of testcases.Then first line of each of the T contains two positive integer V and E where 'V' is the number of vertex and 'E' is number of edges in graph. Next, 'E' line contains two positive numbers showing that there is an edge between these two vertex.
Output:
For each vertex, print their connected nodes in order you are pushing them inside the list .
#include<iostream>
#include<unordered_map>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--){
int nv,ne;
cin>>nv>>ne;
vector<pair<int,int>> vect;
for(int i=0;i<ne;i++)
{ int k,l;
cin>>k>>l;vect.push_back( make_pair(k,l) );
}
unordered_map<int,vector<int>> umap;
for(int i=0;i<ne;i++)
{
umap[vect[i].first].push_back(vect[i].second);
umap[vect[i].second].push_back(vect[i].first);
}
for(int i=0;i<nv;i++)
{
sort(umap[i].begin(),umap[i].end());
}
int j=0;
for(int i=0;i<nv;i++)
{
cout<<i<<"->"<<" ";
for( j=0;j<umap[i].size()-1;j++)
{
cout<<umap[i][j]<<"->"<<" ";
}
cout<<umap[i][j];
cout<<"\n";
}
}
return 0;
}
Example:
Input:
1
5 7
0 1
0 4
1 2
1 3
1 4
2 3
3 4
Output:
0-> 1-> 4
1-> 0-> 2-> 3-> 4
2-> 1-> 3
3-> 1-> 2-> 4
4-> 0-> 1-> 3
"segmentation fault" is caused when a vertex has no edges. I do not see any constraints that all vertices have at least one edge in the problem description. For example, let's consider this input.
1
3 1
0 1
Here vertex 2 does not have any edges. Let's take a look what happens in the printing loop.
for(int i=0;i<nv;i++)
{
cout<<i<<"->"<<" ";
for( j=0;j<umap[i].size()-1;j++)
{
cout<<umap[i][j]<<"->"<<" ";
}
cout<<umap[i][j];
cout<<"\n";
}
umap[i].size()-1 is dangerous : As vector<T>::size() returns an unsigned integer. So if the size is 0 then it is 0-1 and that causes underflow
Even if the first was solved (something like (int)umap[i].size()-1), the following line cout<<umap[i][j]; will try to print umap[i][0] which is invalid if the size is 0
So I would change that code like:
for(int i=0;i<nv;i++)
{
cout<<i;
for( j=0;j<umap[i].size();j++)
{
cout<<"->"<<" "<<umap[i][j];
}
cout<<"\n";
}
I was reading about this and thought to form an algorithm to find the minimum number of moves to solve this.
Constraints I made: An N X N matrix having one empty slot ,say 0, would be plotted having numbers 0 to n-1.
Now we have to recreate this matrix and form the matrix having numbers in increasing order from left to right beginning from the top row and have the last element 0 i.e. (N X Nth)element.
For example,
Input :
8 4 0
7 2 5
1 3 6
Output:
1 2 3
4 5 6
7 8 0
Now the problem is how to do this in minimum number of steps possible.
As in game(link provided) you can either move left, right, up or bottom and shift the 0(empty slot) to corresponding position to make the final matrix.
The output to printed for this algorithm is number of steps say M and then Tile(number) moved in the direction say, 1 for swapping with upper adjacent element, 2 for lower adjacent element, 3 for left adjacent element and 4 for right adjacent element.
Like, for
2 <--- order of N X N matrix
3 1
0 2
Answer should be: 3 4 1 2 where 3 is M and 4 1 2 are steps to tile movement.
So I have to minimise the complexity for this algorithm and want to find minimum number of moves. Please suggest me the most efficient approach to solve this algorithm.
Edit:
What I coded in c++, Please see the algorithm rather than pointing out other issues in code .
#include <bits/stdc++.h>
using namespace std;
int inDex=0,shift[100000],N,initial[500][500],final[500][500];
struct Node
{
Node* parent;
int mat[500][500];
int x, y;
int cost;
int level;
};
Node* newNode(int mat[500][500], int x, int y, int newX,
int newY, int level, Node* parent)
{
Node* node = new Node;
node->parent = parent;
memcpy(node->mat, mat, sizeof node->mat);
swap(node->mat[x][y], node->mat[newX][newY]);
node->cost = INT_MAX;
node->level = level;
node->x = newX;
node->y = newY;
return node;
}
int row[] = { 1, 0, -1, 0 };
int col[] = { 0, -1, 0, 1 };
int calculateCost(int initial[500][500], int final[500][500])
{
int count = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (initial[i][j] && initial[i][j] != final[i][j])
count++;
return count;
}
int isSafe(int x, int y)
{
return (x >= 0 && x < N && y >= 0 && y < N);
}
struct comp
{
bool operator()(const Node* lhs, const Node* rhs) const
{
return (lhs->cost + lhs->level) > (rhs->cost + rhs->level);
}
};
void solve(int initial[500][500], int x, int y,
int final[500][500])
{
priority_queue<Node*, std::vector<Node*>, comp> pq;
Node* root = newNode(initial, x, y, x, y, 0, NULL);
Node* prev = newNode(initial,x,y,x,y,0,NULL);
root->cost = calculateCost(initial, final);
pq.push(root);
while (!pq.empty())
{
Node* min = pq.top();
if(min->x > prev->x)
{
shift[inDex] = 4;
inDex++;
}
else if(min->x < prev->x)
{
shift[inDex] = 3;
inDex++;
}
else if(min->y > prev->y)
{
shift[inDex] = 2;
inDex++;
}
else if(min->y < prev->y)
{
shift[inDex] = 1;
inDex++;
}
prev = pq.top();
pq.pop();
if (min->cost == 0)
{
cout << min->level << endl;
return;
}
for (int i = 0; i < 4; i++)
{
if (isSafe(min->x + row[i], min->y + col[i]))
{
Node* child = newNode(min->mat, min->x,
min->y, min->x + row[i],
min->y + col[i],
min->level + 1, min);
child->cost = calculateCost(child->mat, final);
pq.push(child);
}
}
}
}
int main()
{
cin >> N;
int i,j,k=1;
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
cin >> initial[j][i];
}
}
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
final[j][i] = k;
k++;
}
}
final[N-1][N-1] = 0;
int x = 0, y = 1,a[100][100];
solve(initial, x, y, final);
for(i=0;i<inDex;i++)
{
cout << shift[i] << endl;
}
return 0;
}
In this above code I am checking for each child node which has the minimum cost(how many numbers are misplaced from the final matrix numbers).
I want to make this algorithm further efficient and reduce it's time complexity. Any suggestions would be appreciable.
While this sounds a lot like a homework problem, I'll lend a bit of help.
For significantly small problems, like your 2x2 or 3x3, you can just brute force it. Basically, you do every possible combination with every possible move, track how many turns each took, and then print out the smallest.
To improve on this, maintain a list of solved solutions, and then any time you make a possible move, if that moves already done, stop trying that one since it can't possible be the smallest.
Example, say I'm in this state (flattening your matrix to a string for ease of display):
5736291084
6753291084
5736291084
Notice that we're back to a state we've seen before. That means it can't possible be the smallest move, because the smallest would be done without returning to a previous state.
You'll want to create a tree doing this, so you'd have something like:
134
529
870
/ \
/ \
/ \
/ \
134 134
529 520
807 879
/ | \ / | \
/ | X / X \
134 134 134 134 134 130
509 529 529 502 529 524
827 087 870 879 870 879
And so on. Notice I marked some with X because they were duplicates, and thus we wouldn't want to pursue them any further since we know they can't be the smallest.
You'd just keep repeating this until you've tried all possible solutions (i.e., all non-stopped leaves reach a solution), then you just see which was the shortest. You could also do it in parallel so you stop once any one has found a solution, saving you time.
This brute force approach won't be effective against large matrices. To solve those, you're looking at some serious software engineering. One approach you could take with it would be to break it into smaller matrices and solve that way, but that may not be the best path.
This is a tricky problem to solve at larger values, and is up there with some of the trickier NP problems out there.
Start from solution, determine ranks of permuation
The reverse of above would be how you can pre-generate a list of all possible values.
Start with the solution. That has a rank of permutation of 0 (as in, zero moves):
012
345
678
Then, make all possible moves from there. All of those moves have rank of permutation of 1, as in, one move to solve.
012
0 345
678
/ \
/ \
/ \
102 312
1 345 045
678 678
Repeat that as above. Each new level all has the same rank of permutation. Generate all possible moves (in this case, until all of your branches are killed off as duplicates).
You can then store all of them into an object. Flattening the matrix would make this easy (using JavaScript syntax just for example):
{
'012345678': 0,
'102345678': 1,
'312045678': 1,
'142305678': 2,
// and so on
}
Then, to solve your question "minimum number of moves", just find the entry that is the same as your starting point. The rank of permutation is the answer.
This would be a good solution if you are in a scenario where you can pre-generate the entire solution. It would take time to generate, but lookups would be lightning fast (this is similar to "rainbow tables" for cracking hashes).
If you must solve on the fly (without pre-generation), then the first solution, start with the answer and work your way move-by-move until you find a solution would be better.
While the maximum complexity is O(n!), there are only O(n^2) possible solutions. Chopping off duplicates from the tree as you go, your complexity will be somewhere in between those two, probably in the neighborhood of O(n^3) ~ O(2^n)
You can use BFS.
Each state is one vertex, and there is an edge between two vertices if they can transfer to each other.
For example
8 4 0
7 2 5
1 3 6
and
8 0 4
7 2 5
1 3 6
are connected.
Usually, you may want to use some numbers to represent your current state. For small grid, you can just follow the sequence of the number. For example,
8 4 0
7 2 5
1 3 6
is just 840725136.
If the grid is large, you may consider using the rank of the permutation of the numbers as your representation of the state. For example,
0 1 2
3 4 5
6 7 8
should be 0, as it is the first in permutation.
And
0 1 2
3 4 5
6 7 8
(which is represented by 0)
and
1 0 2
3 4 5
6 7 8
(which is represented by some other number X)
are connected is the same as 0 and X are connected in the graph.
The complexity of the algo should be O(n!) as there are at most n! vertices/permutations.
I recently came across this question and thought I could share it here, since I wasn't able to get it.
We are given a 5*5 grid numbered from 1-25, and a set of 5 pairs of points,that are start and end points of a path on the grid.
Now we need to find 5 corresponding paths for the 5 pairs of points, such that no two paths should overlap. Also note that only vertical and horizontal moves are allowed. Also the combined 5 path should cover the entire grid.
For example we are given the pair of points as:
P={1,22},{4,17},{5,18},{9,13},{20,23}
Then the corresponding paths will be
1-6-11-16-21-22
4-3-2-7-12-17
5-10-15-14-19-18
9-8-13
20-25-24-23
What I have thought of so far:
Maybe i can compute all paths from source to destination for all pairs of points and then check if there's no common point in the paths. However this seems to be of higher time complexity.
Can anyone propose a better algorithm? I would be glad if one could explain through a pseudo code.Thanks
This problem is essentially the Hamiltonian path/cycle problem problem (since you can connect the end of one path to the start of another, and consider all the five paths as a part of one big cycle). There are no known efficient algorithms for this, as the problem is NP-complete, so you do essentially need to try all possible paths with backtracking (there are fancier algorithms, but they're not much faster).
Your title asks for an approximation algorithm, but this is not an optimization problem - it's not the case that some solutions are better than others; all correct solutions are equally good, and if it isn't correct, then it's completely wrong - so there is no possibility for approximation.
Edit: The below is a solution to the original problem posted by the OP, which did not include the "all cells must be covered" constraint. I'm leaving it up for those that might face the original problem.
This can be solved with a maximum flow algorithm, such as Edmonds-Karp.
The trick is to model the grid as a graph where there are two nodes per grid cell; one "outgoing" node and one "incoming" node. For each adjacent pair of cells, there are edges from the "outgoing" node in either cell to the "incoming" node in the other cell. Within each cell, there is also an edge from the "incoming" to the "outgoing" node. Each edge has the capacity 1. Create one global source node that has an edge to all the start nodes, and one global sink node to which all end nodes have an edge.
Then, run the flow algorithm; the resulting flow shows the non-intersecting paths.
This works because all flow coming in to a cell must pass through the "internal" edge from the "incoming" to the "ougoing" node, and as such, the flow through each cell is limited to 1 - therefore, no paths will intersect. Also, Edmonds-Karp (and all Floyd-Warshall based flow algorithms) will produce integer flows as long as all capacities are integers.
Here's a program written in Python that walks all potential paths. It uses recursion and backtracking to find the paths, and it marks a grid to see which locations are already being used.
One key optimization is that it marks the start and end points on the grid (10 of the 25 points).
Another optimization is that it generates all moves from each point before starting the "walk" across the grid. For example, from point 1 the moves are to points 2 & 6; from point 7, the moves are to points 2, 6, 8 & 12.
points = [(1,22), (4,17), (5,18), (9,13), (20,23)]
paths = []
# find all moves from each position 0-25
moves = [None] # set position 0 with None
for i in range(1,26):
m = []
if i % 5 != 0: # move right
m.append(i+1)
if i % 5 != 1: # move left
m.append(i-1)
if i > 5: # move up
m.append(i-5)
if i < 21: # move down
m.append(i+5)
moves.append(m)
# Recursive function to walk path 'p' from 'start' to 'end'
def walk(p, start, end):
for m in moves[start]: # try all moves from this point
paths[p].append(m) # keep track of our path
if m == end: # reached the end point for this path?
if p+1 == len(points): # no more paths?
if None not in grid[1:]: # full coverage?
print
for i,path in enumerate(paths):
print "%d." % (i+1), '-'.join(map(str, path))
else:
_start, _end = points[p+1] # now try to walk the next path
walk(p+1, _start, _end)
elif grid[m] is None: # can we walk onto the next grid spot?
grid[m] = p # mark this spot as taken
walk(p, m, end)
grid[m] = None # unmark this spot
paths[p].pop() # backtrack on this path
grid = [None for i in range(26)] # initialize the grid as empty points
for p in range(len(points)):
start, end = points[p]
paths.append([start]) # initialize path with its starting point
grid[start] = grid[end] = p # optimization: pre-set the known points
start, end = points[0]
walk(0, start, end)
Well, I started out thinking about a brute force algorithm, and I left that below, but it turns out it's actually simpler to search for all answers rather than generate all configurations and test for valid answers. Here's the search code, which ended up looking much like #Brent Washburne's. It runs in 53 milliseconds on my laptop.
import java.util.Arrays;
class Puzzle {
final int path[][];
final int grid[] = new int[25];
Puzzle(int[][] path) {
// Make the path endpoints 0-based for Java arrays.
this.path = Arrays.asList(path).stream().map(pair -> {
return new int[] { pair[0] - 1, pair[1] - 1 };
}).toArray(int[][]::new);
}
void print() {
System.out.println();
for (int i = 0; i < grid.length; i += 5)
System.out.println(
Arrays.toString(Arrays.copyOfRange(grid, i, i + 5)));
}
void findPaths(int ip, int i) {
if (grid[i] != -1) return; // backtrack
grid[i] = ip; // mark visited
if(i == path[ip][1]) // path complete
if (ip < path.length - 1) findPaths(ip + 1, path[ip + 1][0]); // find next path
else print(); // solution complete
else { // continue with current path
if (i < 20) findPaths(ip, i + 5);
if (i > 4) findPaths(ip, i - 5);
if (i % 5 < 4) findPaths(ip, i + 1);
if (i % 5 > 0) findPaths(ip, i - 1);
}
grid[i] = -1; // unmark
}
void solve() {
Arrays.fill(grid, -1);
findPaths(0, path[0][0]);
}
public static void main(String[] args) {
new Puzzle(new int[][]{{1, 22}, {4, 17}, {5, 18}, {9, 13}, {20, 23}}).solve();
}
}
Old, bad answer
This problem is doable by brute force if you think about it "backward:" assign all the grid squares to paths and test to see if the assignment is valid. There are 25 grid squares and you need to construct 5 paths, each with 2 endpoints. So you know the paths these 10 points lie on. All that's left is to label the remaining 15 squares with the paths they lie on. There are 5 possibilities for each, so 5^15 in all. That's about 30 billion. All that's left is to build an efficient checker that says whether a given assignment is a set of 5 valid paths. This is simple to do by linear time search. The code below finds your solution in about 2 minutes and takes a bit under 11 minutes to test exhaustively on my MacBook:
import java.util.Arrays;
public class Hacking {
static class Puzzle {
final int path[][];
final int grid[] = new int[25];
Puzzle(int[][] path) { this.path = path; }
void print() {
System.out.println();
for (int i = 0; i < grid.length; i += 5)
System.out.println(
Arrays.toString(Arrays.copyOfRange(grid, i, i + 5)));
}
boolean trace(int p, int i, int goal) {
if (grid[i] != p) return false;
grid[i] = -1; // mark visited
boolean rtn =
i == goal ? !Arrays.asList(grid).contains(p) : nsew(p, i, goal);
grid[i] = p; // unmark
return rtn;
}
boolean nsew(int p, int i, int goal) {
if (i < 20 && trace(p, i + 5, goal)) return true;
if (i > 4 && trace(p, i - 5, goal)) return true;
if (i % 5 < 4 && trace(p, i + 1, goal)) return true;
if (i % 5 > 0 && trace(p, i - 1, goal)) return true;
return false;
}
void test() {
for (int ip = 0; ip < path.length; ip++)
if (!trace(ip, path[ip][0] - 1, path[ip][1] - 1)) return;
print();
}
void enumerate(int i) {
if (i == grid.length) test();
else if (grid[i] != -1) enumerate(i + 1); // already known
else {
for (int ip = 0; ip < 5; ip++) {
grid[i] = ip;
enumerate(i + 1);
}
grid[i] = -1;
}
}
void solve() {
Arrays.fill(grid, -1);
for (int ip = 0; ip < path.length; ip++)
grid[path[ip][0] - 1] = grid[path[ip][1] - 1] = ip;
enumerate(0);
}
}
public static void main(String[] args) {
new Puzzle(new int[][]{{1, 22}, {4, 17}, {5, 18}, {9, 13}, {20, 23}}).solve();
}
}
The starting array:
[ 0, -1, -1, 1, 2]
[-1, -1, -1, 3, -1]
[-1, -1, 3, -1, -1]
[-1, 1, 2, -1, 4]
[-1, 0, 4, -1, -1]
The result:
[ 0, 1, 1, 1, 2]
[ 0, 1, 3, 3, 2]
[ 0, 1, 3, 2, 2]
[ 0, 1, 2, 2, 4]
[ 0, 0, 4, 4, 4]
I had a test right now and this was one of the questions:
Input
The places to visit in the labyrinth are numbered from 1 to n. The entry and
the exit correspond to number 1 and number n, respectively; the remaining
numbers correspond to crossings. Note that there are no dead ends and
there is no more than one connection linking a pair of crossings.
For each test case, the first line gives n and the number of connections
between crossings (m). Then, in each of the following m lines, you find a pair
of integers corresponding to the connection between two crossings.
Output
For each test case, your implementation should output one single line
containing "Found!", if it is possible to reach the exit by visiting every
crossing once or "Damn!", otherwise. Other test cases may follow.
Constraints
m < 32
n < 21
Example input:
8 13
1 2
1 3
2 3
2 4
3 4
3 5
4 5
4 6
5 6
5 7
6 7
6 8
7 8
8 8
1 2
1 3
2 4
3 5
4 6
5 7
6 8
7 8
Example output:
Found!
Damn!
I solved the problem using a sort of DFS algorithm but i have a few questions.
Using DFS algorithm, I implemented a recursive function that starts in the given node and tries to visit every node once and the last node must be the exit node. I don't have the full code right now but but it was something like this:
findPath(int current node, int numVisitedNodes, int *visited){
int *tmpVisited = copyArray(visited); //copies the visited array to tmpVisited
//DFS algo here
}
Every recursive call it copies the visited nodes array. I'm doing this because when it finds an invalid path and the recursion goes back to the origin, it can still go because no one overwrote the visited nodes list.
Is there any better way to do this?
How would you solve it? (you can provide code if you want)
Read the crossing
if start or end of the crossing belongs to a reachable set, add both to that set else create a new reachable set.
When input has finished, check if any of the reachable sets contains
both entrance and exit points
HashSet operations complexity is O(1). If every crossing are distinct, complexity is O(n^2),which is the worst case complexity of this algorithm. Space complexity is O(n), there is no recursion so there is no recursion overhead of memory.
Roughly speaking, every node is visited only once.
Java code using valid reachable sets is as follows.
public class ZeManel {
public static void main(String[] args) {
Integer[][] input = {{1,2},{2,3},{4,6}};
zeManel(input);
}
public static void zeManel(Integer[][] input){
List<Set<Integer>> paths = new ArrayList<Set<Integer>>();
int max = 0;
for(int i = 0;i < input.length;i++) {
max = input[i][0] > max ? input[i][0] : max;
max = input[i][1] > max ? input[i][1] : max;
boolean inPaths = false;
for (Set<Integer> set : paths) {
if(set.contains(input[i][0]) || set.contains(input[i][1])) {
set.add(input[i][0]);
set.add(input[i][1]);
inPaths = true;
break;
}
}
if(!inPaths) {
Set<Integer> path = new HashSet<Integer>();
path.add(input[i][0]);
path.add(input[i][1]);
paths.add(path);
}
}
for (Set<Integer> path : paths) {
if(path.contains(1) && path.contains(max)) {
System.out.println("Found!");
return;
}
}
System.out.println("Damn!");
}
}
This was my implementation during the test:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
# define N 21
# define M 32
int i;
int adj[N][N];
int count = 0;
int findPath(int numNodes, int currentNode, int depth, int *visited){
visited[currentNode] = 1;
if(currentNode == numNodes - 1 && depth == numNodes){
return 1;
}
if(depth > numNodes)
return -1;
int r = -1;
if(depth < numNodes){
count++;
int *tmp = (int*) malloc(numNodes*sizeof(int));
for(i = 0; i < numNodes; i++)
tmp[i] = visited[i];
for(i = 0; i < numNodes; i++){
if(adj[currentNode][i] == 1 && tmp[i] == 0 && r == -1){
if(findPath(numNodes, i, depth + 1, tmp) == 1)
r = 1;
}
}
free(tmp);
}
return r;
}
int main(){
int numLigacoes, a, b, numNodes;
int *visited;
while (scanf("%d %d", &numNodes, &numLigacoes) != EOF){
visited = (int*) malloc(numNodes*sizeof(int));
count = 0;
memset(adj, 0, N*N*sizeof(int));
memset(visited, 0, numNodes*sizeof(int));
for (i = 0; i < numLigacoes; i++){
scanf("%d %d", &a, &b);
adj[a - 1][b - 1] = 1;
adj[b - 1][a - 1] = 1;
}
if(findPath(numNodes, 0, 1, visited) == 1)
printf("Found! (%d)\n", count);
else
printf("Damn! (%d)\n", count);
free(visited);
}
return 0;
}
What do you think about that?