I'm trying to solve the following question:
https://leetcode.com/problems/bus-routes/
We have a list of bus routes. Each routes[i] is a bus route that the ith bus repeats forever. For example if routes[0] = [1, 5, 7], this means that the first bus (0th indexed) travels in the sequence 1→5→7→1→5→7→1→... forever.
We start at bus stop S (initially not on a bus), and we want to go to bus stop T. Travelling by buses only, what is the least number of buses we must take to reach our destination? Return -1 if it is not possible.
Example:
Input:
routes = [[1, 2, 7], [3, 6, 7]]
S = 1
T = 6
Output:
2
Explanation:
The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.
Note:
1 <= routes.length <= 500.
1 <= routes[i].length <= 500.
0 <= routes[i][j] < 10 ^ 6.
My idea is to treat each stop as a Node. Each node has a color (the bus number), and has a value (the stop number).
This problem would then be converted to a 0-1 BFS shortest path problem.
Here's my code :
class Node {
int val;
int color;
boolean visited;
int distance;
public Node(int val, int color) {
this.val = val;
this.color = color;
this.visited = false;
this.distance = 0;
}
public String toString() {
return "{ val = " + this.val + ", color = " + this.color + " ,distance = " + this.distance + "}";
}
}
class Solution {
public int numBusesToDestination(int[][] routes, int S, int T) {
if(S == T) return 0;
// create nodes
// map each values node(s)
// distance between nodes of the same bus, have 0 distance
// if you're switching buses, the distance is 1
Map<Integer, List<Node>> adjacency = new HashMap<Integer, List<Node>>();
int color = 0;
Set<Integer> colorsToStartWith = new HashSet<Integer>();
for(int[] route : routes) {
for(int i = 0; i < route.length - 1; i++) {
int source = route[i];
int dest = route[i + 1];
adjacency.putIfAbsent(source, new ArrayList<Node>());
adjacency.putIfAbsent(dest, new ArrayList<Node>());
if(source == S) colorsToStartWith.add(color);
adjacency.get(source).add(new Node(dest, color));
adjacency.get(source).add(new Node(source, color));
}
if(route[route.length - 1] == S) colorsToStartWith.add(color);
adjacency.putIfAbsent(route[route.length - 1], new ArrayList<Node>());
adjacency.get(route[route.length - 1]).add(new Node(route[0], color));
adjacency.get(route[route.length - 1]).add(new Node(route[route.length - 1], color));
color++;
}
// run bfs
int minDistance = Integer.MAX_VALUE;
Deque<Node> q = new LinkedList<Node>();
Node start = new Node(S, 0);
start.distance = 1;
q.add(start);
boolean first = true;
boolean found = false;
while(!q.isEmpty()) {
Node current = q.remove();
current.visited = true;
System.out.println(current);
for(Node neighbor : adjacency.get(current.val)) {
if(!neighbor.visited) {
neighbor.visited = true;
if(neighbor.color == current.color || current.val == neighbor.val || first) {
q.addFirst(neighbor);
neighbor.distance = current.distance;
} else {
q.addLast(neighbor);
neighbor.distance = current.distance + 1;
}
if(neighbor.val == T) {
minDistance = Math.min(minDistance, neighbor.distance);
}
}
}
first = false;
}
return minDistance == Integer.MAX_VALUE ? -1 : minDistance;
}
}
I'm not sure why this is wrong.
The following test case fails :
Routes = [
[12,16,33,40,44,47,68,69,77,78,82,86,97],
[5,8,25,28,45,46,50,52,63,66,80,81,95,97],
[4,5,6,14,30,31,34,36,37,47,48,55,56,58,73,74,76,80,88,98],
[58,59],
[54,56,78,96,98],
[7,30,35,44,60,87,97],
[3,5,57,88],
[3,9,13,15,23,24,28,38,49,51,54,59,63,65,78,81,86,92,95],
[2,7,16,20,23,46,55,57,93],
[10,11,15,31,32,48,53,54,57,66,69,75,85,98],
[24,26,30,32,51,54,58,77,81],
[7,21,39,40,49,58,84,89],
[38,50,57],
[10,57],
[11,27,28,37,55,56,58,59,81,87,97],
[0,1,8,17,19,24,25,27,36,37,39,51,68,72,76,82,84,87,89],
[10,11,14,22,26,30,48,49,62,66,79,80,81,85,89,93,96,98],
[16,18,24,32,35,37,46,63,66,69,78,80,87,96],
[3,6,13,14,16,17,29,30,42,46,58,73,77,78,81],
[15,19,32,37,52,57,58,61,69,71,73,92,93]
]
S = 6
T = 30
What is the error in my code that makes this test fail?
The example input you give should return 1, since the one-but-last route contains both the source and target bus stop (6 and 30):
[3,6,13,14,16,17,29,30,42,46,58,73,77,78,81]
I ran your code with that input, and it returns 1, so your solution is rejected for another reason, which then must be a time out.
I see several causes for why your code is not optimal:
While ideally a BFS should stop when it has found the target node, your version must continue to visit all unvisited nodes that are reachable, before it can decide what the solution is. So even if it finds the target on the same route as the source, it will continue to switch routes and so do a lot of unnecessary work, as there is no hope to find a shorter path.
This is not how it is supposed to be. You should take care to perform your search in a way that gives priority to the edges that do not increase the distance, and only when there are no more of those, pick an edge that adds 1 to the distance. If you do it like that you can stop the search as soon as you have found the target.
A Node object is created repeatedly for the same combination of bus stop and "color" (i.e. route). As a consequence, when you later set visited to true, the duplicate Node objects will still have visited equal to false and so that bus stop will be visited several times with no gain.
You should make sure to only create new Node objects when there is no existing object with such combination yet.
Edges exist between two consecutive bus stops on the same route, meaning you may need to traverse several edges on the same route before finding the one that is either the target or the right place to switch to another route.
It would be more efficient to consider a whole route a Node: routes would be considered connected (with an edge) when they share at least one bus stop. Converting routes to Sets of bus stops would make it fast and easy to identify these edges.
The reflexive edges, from and to the same bus stop, but specifying a color (route), also do not add to efficiency. The main issue you tried to solve with this set up, is to make sure that the first choice of a route is free of charge (is not considered a switch). But that concern is no longer an issue if you apply the previous bullet point.
Implementation
I chose JavaScript as implementation, but I guess it wont be hard to rewrite this in Java:
function numBusesToDestination (routes, S, T) {
if (S === T) return 0;
// Create nodes of the graph
const nodes = routes;
// Map bus stops to routes: a map keyed by stops, with each an empty Set as value
const nodesAtBusStop = new Map([].concat(...routes.map(route => route.map(stop => [stop, new Set]))));
// ... and populate those empty Sets:
for (let node of nodes) {
for (let stop of node) {
nodesAtBusStop.get(stop).add(node);
}
}
// Build adjacency list of the graph
const adjList = new Map(nodes.map(node => [node, new Set]));
for (let [stop, nodes] of nodesAtBusStop.entries()) {
for (let a of nodes) {
for (let b of nodes) {
if (a !== b) adjList.get(a).add(b);
}
}
}
const startNodes = nodesAtBusStop.get(S);
const targetNodes = nodesAtBusStop.get(T);
if (!startNodes || !targetNodes) return -1;
// BFS
let queue = [...startNodes];
let distance = 1;
let visited = new Set;
while (queue.length) {
// Create a new queue for each distance increment
let nextLevel = [];
for (let node of queue) {
if (visited.has(node)) continue;
visited.add(node);
if (targetNodes.has(node)) return distance;
nextLevel.push(...adjList.get(node));
}
queue = nextLevel;
distance++;
}
return -1;
};
// I/O handling
(document.oninput = function () {
let result = "invalid JSON";
try {
let routes = JSON.parse(document.querySelector("#inputRoutes").value);
let S = +document.querySelector("#inputStart").value;
let T = +document.querySelector("#inputTarget").value;
result = numBusesToDestination(routes, S, T);
}
catch (e) {}
document.querySelector("#output").textContent = result;
})();
#inputRoutes { width: 100% }
Routes in JSON format:
<textarea id="inputRoutes">[[1,2,7],[3,6,7]]</textarea><br>
Start: <input id="inputStart" value="1"><br>
Target: <input id="inputTarget" value="6"><br>
Distance: <span id="output"></span>
I am trying to solve this question.
I am able to find by seeing the degrees that the given structure can form euler circuit or not but I am unable to figure out how to find trace all path, for the given test case
5
2 1
2 2
3 4
3 1
2 4
there is one loop in the circuit at node 2, which I don't know how to trace, If I am using adjacency list representation then I'll get following list
1: 2,3
2: 1,2,2,4
3: 1,4
4: 2,3
So how to traverse every edge, I know it is euler circuit problem, but that self loop thing is making tough for me to code and I am not getting any tutorial or blog from where I can understand this thing.
I again thought to delete the nodes from adjacency list once I traverse that path( in order to maintain the property of euler(path should be traversed once)), but I am using vector for storing adjacency list and I don't know how to delete particular element from vector. I googled it and found remove command to delete from vectors but remove deletes all matching element from the vector.
I tried to solve the problem as below now, but getting WA :(
#include<iostream>
#include<cstdio>
#include<cstring>
int G[52][52];
int visited[52],n;
void printadj() {
int i,j;
for(i=0;i<51;i++) {
for(j=0;j<51;j++)
printf("%d ",G[i][j]);
printf("\n");
}
}
void dfs(int u){
int v;
for(v=0;v<51;v++){
if(G[u][v]){
G[u][v]--;
G[v][u]--;
printf("%d %d\n",u,v);
dfs(v);
}
}
}
bool is_euler(){
int i,j,colsum=0,count=0;
for(i=0;i<51;i++) {
colsum=0;
for(j=0;j<51;j++) {
if(G[i][j] > 0) {
colsum+=G[i][j];
}
}
if(colsum%2!=0) count++;
}
// printf("\ncount=%d\n",count);
if(count >0 ) return false;
else return true;
}
void reset(){
int i,j;
for(i=0;i<51;i++)
for(j=0;j<51;j++)
G[i][j]=0;
}
int main(){
int u,v,i,t,k;
scanf("%d",&t);
for(k=0;k<t;k++) {
scanf("%d",&n);
reset();
for(i=0;i<n;i++){
scanf("%d%d",&u,&v);
G[u][v]++;
G[v][u]++;
}
// printadj();
printf("Case #%d\n",k+1);
if(is_euler()) {
dfs(u);
}
else printf("some beads may be lost\n");
printf("\n");
}
return 0;
}
Dont know why getting WA :(
New Code:-
#include<iostream>
#include<cstdio>
#include<cstring>
#define max 51
int G[max][max],print_u[max],print_v[max],nodes_traversed[max],nodes_found[max];
int n,m;
void printadj() {
int i,j;
for(i=0;i<max;i++) {
for(j=0;j<max;j++)
printf("%d ",G[i][j]);
printf("\n");
}
}
void dfs(int u){
int v;
for(v=0;v<50;v++){
if(G[u][v]){
G[u][v]--;
G[v][u]--;
print_u[m]=u;
print_v[m]=v;
m++;
dfs(v);
}
}
nodes_traversed[u]=1;
}
bool is_evendeg(){
int i,j,colsum=0,count=0;
for(i=0;i<50;i++) {
colsum=0;
for(j=0;j<50;j++) {
if(G[i][j] > 0) {
colsum+=G[i][j];
}
}
if(colsum&1) return false;
}
return true;
}
int count_vertices(int nodes[]){
int i,count=0;
for(i=0;i<51;i++) if(nodes[i]==1) count++;
return count;
}
void reset(){
int i,j;
m=0;
for(i=0;i<max;i++)
for(j=0;j<max;j++)
G[i][j]=0;
memset(print_u,0,sizeof(print_u));
memset(print_v,0,sizeof(print_v));
memset(nodes_traversed,0,sizeof(nodes_traversed));
memset(nodes_found,0,sizeof(nodes_found));
}
bool is_connected(int tot_nodes,int trav_nodes) {
if(tot_nodes == trav_nodes) return true;
else return false;
}
int main(){
int u,v,i,t,k,tot_nodes,trav_nodes;
scanf("%d",&t);
for(k=0;k<t;k++) {
scanf("%d",&n);
reset();
for(i=0;i<n;i++){
scanf("%d%d",&u,&v);
G[u][v]++;
G[v][u]++;
nodes_found[u]=nodes_found[v]=1;
}
// printadj();
printf("Case #%d\n",k+1);
tot_nodes=count_vertices(nodes_found);
if(is_evendeg()) {
dfs(u);
trav_nodes=count_vertices(nodes_traversed);
if(is_connected(tot_nodes,trav_nodes)) {
for(i=0;i<m;i++)
printf("%d %d\n",print_u[i],print_v[i]);
}
else printf("some beads may be lost\n");
}
else printf("some beads may be lost\n");
printf("\n");
}
return 0;
}
This code is giving me runtime error there, please look into the code.
What you need to do is form arbitrary cycles and then connect all cycles together. You seem to be doing only one depth first traversal, which might give you a Eulerian circuit, but it also may give you a 'shortcut' of an Eulerian circuit. That is because in every vertex where the Eulerian circuit passes more then once (i.e., where it crosses itself), when the depth first traversal arrives there for the first time, it may pick the edge that leads directly back to the start of the depth first traversal.
Thus, you're algorithm should consist of two parts:
Find all cycles
Connect the cycles together
If done right, you don't even have to check that all vertices have an even degree, instead you can rely on the fact that if step 1 or 2 cannot continue anymore, there exists no Eulerian cycle.
Reference Implementation (Java)
Since there's no language tag in your question, I'm going to assume that it's fine for you that I'll give you a Java reference implementation. Furthermore, I'll use the term 'node' instead of 'vertex', but that's just personal preference (it gives shorter code ;)).
I'll use one constant in this algorithm, which I will refer to from the other classes:
public static final int NUMBER_OF_NODES = 50;
Then, we'll need an Edge class to easily construct our cycles, which are basically linked lists of edges:
public class Edge
{
int u, v;
Edge prev, next;
public Edge(int u, int v)
{
this.u = u;
this.v = v;
}
/**
* Attaches a new edge to this edge, leading to the given node
* and returns the newly created Edge. The node where the
* attached edge starts doesn't need to be given, as it will
* always be the node where this edge ends.
*
* #param node The node where the attached edge ends.
*/
public Edge attach(int node)
{
next = new Edge(this.v, node);
next.prev = this;
return next;
}
}
Then, we'll need a Cycle class that can easily join two cycles:
public class Cycle
{
Edge start;
boolean[] used = new boolean[NUMBER_OF_NODES+1];
public Cycle(Edge start)
{
// Store the cycle itself
this.start = start;
// And memorize which nodes are being used in this cycle
used[start.u] = true;
for (Edge e = start.next; e != start; e = e.next)
used[e.u] = true;
}
/**
* Checks if this cycle can join with the given cycle. That is
* the case if and only if both cycles use a common node.
*
* #return {#code true} if this and that cycle can be joined,
* {#code false} otherwise.
*/
public boolean canJoin(Cycle that)
{
// Find a commonly used node
for (int node = 1; node <= NUMBER_OF_NODES; node++)
if (this.used[node] && that.used[node])
return true;
return false;
}
/**
* Joins the given cycle to this cycle. Both cycles will be broken
* at a common node and the paths will then be connected to each
* other. The given cycle should not be used after this call, as the
* list of used nodes is most probably invalidated, only this cycle
* will be updated and remains valid.
*
* #param that The cycle to be joined to this cycle.
*/
public void join(Cycle that)
{
// Find the node where we'll join the two cycles
int junction = 1;
while (!this.used[junction] || !that.used[junction])
junction++;
// Find the join place in this cycle
Edge joinAfterEdge = this.start;
while (joinAfterEdge.v != junction)
joinAfterEdge = joinAfterEdge.next;
// Find the join place in that cycle
Edge joinBeforeEdge = that.start;
while (joinBeforeEdge.u != junction)
joinBeforeEdge = joinBeforeEdge.next;
// Connect them together
joinAfterEdge.next.prev = joinBeforeEdge.prev;
joinBeforeEdge.prev.next = joinAfterEdge.next;
joinAfterEdge.next = joinBeforeEdge;
joinBeforeEdge.prev = joinAfterEdge;
// Update the used nodes
for (int node = 1; node <= NUMBER_OF_NODES; node++)
this.used[node] |= that.used[node];
}
#Override
public String toString()
{
StringBuilder s = new StringBuilder();
s.append(start.u).append(" ").append(start.v);
for (Edge curr = start.next; curr != start; curr = curr.next)
s.append("\n").append(curr.u).append(" ").append(curr.v);
return s.toString();
}
}
Now our utility classes are in place, we can write the actual algorithm (although technically, part of the algorithm is extending a path (Edge.attach(int node)) and joining two cycles (Cycle.join(Cycle that)).
/**
* #param edges A variant of an adjacency matrix: the number in edges[i][j]
* indicates how many links there are between node i and node j. Note
* that this means that every edge contributes two times in this
* matrix: one time from i to j and one time from j to i. This is
* also true in the case of a loop: the link still contributes in two
* ways, from i to j and from j to i, even though i == j.
*/
public static Cycle solve(int[][] edges)
{
Deque<Cycle> cycles = new LinkedList<Cycle>();
// First, find a place where we can start a new cycle
for (int u = 1; u <= NUMBER_OF_NODES; u++)
for (int v = 1; v <= NUMBER_OF_NODES; v++)
if (edges[u][v] > 0)
{
// The new cycle starts at the edge from u to v
Edge first, last = first = new Edge(u, v);
edges[last.u][last.v]--;
edges[last.v][last.u]--;
int curr = last.v;
// Extend the list of edges until we're back at the start
search: while (curr != u)
{
// Find any edge that extends the last edge
for (int next = 1; next <= NUMBER_OF_NODES; next++)
if (edges[curr][next] > 0)
{
// We found an edge, attach it to the last one
last = last.attach(next);
edges[last.u][last.v]--;
edges[last.v][last.u]--;
curr = next;
continue search;
}
// We can't form a cycle anymore, which
// means there is no Eulerian cycle.
return null;
}
// Connect the end to the start
last.next = first;
first.prev = last;
// Save it
cycles.add(new Cycle(last));
// And don't forget about the possibility that there are
// more edges running from u to v, so v should be
// re-examined in the next iteration.
v--;
}
// Now we have put all edges into cycles,
// we join them all together (if possible)
merge: while (cycles.size() > 1)
{
// Join the last cycle with any of the previous ones
Cycle last = cycles.removeLast();
for (Cycle curr : cycles)
if (curr.canJoin(last))
{
// Found one! Just join it and continue the merge
curr.join(last);
continue merge;
}
// No compatible cycle found, meaning there is no Eulerian cycle
return null;
}
return cycles.getFirst();
}
I'd implement Edmond Karp algorithm, but seems it's not correct and I'm not getting correct flow, consider following graph and flow from 4 to 8:
Algorithm runs as follow:
First finds 4→1→8,
Then finds 4→5→8
after that 4→1→6→8
And I think third path is wrong, because by using this path we can't use flow from 6→8 (because it used), and in fact we can't use flow from 4→5→6→8.
In fact if we choose 4→5→6→8, and then 4→1→3→7→8 and then 4→1→3→7→8 we can gain better flow(40).
I Implemented algorithm from wiki sample code. I think we can't use any valid path and in fact this greedy selection is wrong.
Am I wrong?
Code is as below (in c#, threshold is 0, and doesn't affect the algorithm):
public decimal EdmondKarps(decimal[][] capacities/*Capacity matrix*/,
List<int>[] neighbors/*Neighbour lists*/,
int s /*source*/,
int t/*sink*/,
decimal threshold,
out decimal[][] flowMatrix
/*flowMatrix (A matrix giving a legal flowMatrix with the maximum value)*/
)
{
THRESHOLD = threshold;
int n = capacities.Length;
decimal flow = 0m; // (Initial flowMatrix is zero)
flowMatrix = new decimal[n][]; //array(1..n, 1..n) (Residual capacity from u to v is capacities[u,v] - flowMatrix[u,v])
for (int i = 0; i < n; i++)
{
flowMatrix[i] = new decimal[n];
}
while (true)
{
var path = new int[n];
var pathCapacity = BreadthFirstSearch(capacities, neighbors, s, t, flowMatrix, out path);
if (pathCapacity <= threshold)
break;
flow += pathCapacity;
//(Backtrack search, and update flowMatrix)
var v = t;
while (v != s)
{
var u = path[v];
flowMatrix[u][v] = flowMatrix[u][v] + pathCapacity;
flowMatrix[v][u] = flowMatrix[v][u] - pathCapacity;
v = u;
}
}
return flow;
}
private decimal BreadthFirstSearch(decimal[][] capacities, List<int>[] neighbors, int s, int t, decimal[][] flowMatrix, out int[] path)
{
var n = capacities.Length;
path = Enumerable.Range(0, n).Select(x => -1).ToArray();//array(1..n)
path[s] = -2;
var pathFlow = new decimal[n];
pathFlow[s] = Decimal.MaxValue; // INFINT
var Q = new Queue<int>(); // Q is exactly Queue :)
Q.Enqueue(s);
while (Q.Count > 0)
{
var u = Q.Dequeue();
for (int i = 0; i < neighbors[u].Count; i++)
{
var v = neighbors[u][i];
//(If there is available capacity, and v is not seen before in search)
if (capacities[u][v] - flowMatrix[u][v] > THRESHOLD && path[v] == -1)
{
// save path:
path[v] = u;
pathFlow[v] = Math.Min(pathFlow[u], capacities[u][v] - flowMatrix[u][v]);
if (v != t)
Q.Enqueue(v);
else
return pathFlow[t];
}
}
}
return 0;
}
The way to choose paths is not important.
You have to add edges of the path in reverse order with path capacity and reduce capacity of edges of the path by that value.
In fact this solution works:
while there is a path with positive capacity from source to sink{
find any path with positive capacity from source to sink, named P with capacity C.
add C to maximum_flow_value.
reduce C from capacity of edges of P.
add C to capacity of edges of reverse_P.
}
Finally the value of maximum-flow is sum of Cs in the loop.
If you want to see the flow in edges in the maximum-flow you made, you can retain the initial graph somewhere, the flow in edge e would be original_capacity_e - current_capacity_e.