I have tried to solve the problem Rerouting at hacker rank. I am posting here for help as competition is over.
https://www.hackerrank.com/contests/hack-the-interview-v-asia-pacific/challenges/rerouting
I have tried to solve problem using Strong connected components, but test cases failed. I can understand we have to remove cycles. But I stuck how to approach problem. Below is solution i have written. I am looking for guidence how to move forward so that i can apply my knowledge future based on mistakes i made here. Thanks for your time and help
int getMinConnectionChange(vector<int> connection) {
// Idea: Get number of strongly connected components.
int numberOfVertices = connection.size();
for(int idx = 0; idx < numberOfVertices; idx++) {
cout << idx+1 <<":"<< connection[idx] << endl;
}
stack<int> stkVertices;
map<int, bool> mpVertexVisited; //is vertex visited.think this as a chalk mark for nodes visited.
int numOFSCCs = 0;
int currTime = 1;
for (int vertexId = 0; vertexId < numberOfVertices; vertexId++) {
// check if node is already visited.
if (mpVertexVisited.find(vertexId+1) == mpVertexVisited.end()) {
numOFSCCs++;
mpVertexVisited.insert(make_pair(vertexId+1, true));
stkVertices.push(vertexId+1);
currTime++;
while (!stkVertices.empty()) {
int iCurrentVertex = stkVertices.top();
stkVertices.pop();
// get adjacent vertices. In this excercise we have only one neighbour. i.e., edge
int neighbourVertexId = connection[iCurrentVertex-1];
// if vertex is already visisted, don't insert in to stack.
if (mpVertexVisited.find(neighbourVertexId) != mpVertexVisited.end()) {
continue;
}
mpVertexVisited.insert(make_pair(neighbourVertexId, true));
stkVertices.push(neighbourVertexId);
} // while loop
} // if condition m_mapVrtxTimes.find(*itr) == m_mapVrtxTimes.end()
} // for loop of vertices
return numOFSCCs - 1;
}
This is a problem that I just solved and would like to share the solution.
The problem can be solved with union-find.
Two main observation:
The number of edges that has to be changed is the number of components - 1 (not necessarily strongly connected) Thus, union-find is handy here for finding the number of components
Second observation is that some component doesn't have terminating node, consider 1<->2, in other words, a cycle exist. We can detect whether there exists a terminating node if some node doesn't have an outgoing edge.
If all components have a cycle, it means that we need to change every component instead of a number of components - 1. This is to make it such that the graph will have a terminating point.
Code:
struct UF {
vector<int> p, rank, size;
int cnt;
UF(int N) {
p = rank = size = vector<int>(N, 1);
for (int i = 0; i < N; i++) p[i] = i;
cnt = N;
}
int find(int i) {
return p[i] == i ? i : p[i] = find(p[i]);
}
bool connected(int i, int j) {
return find(i) == find(j);
}
void join(int i, int j) {
if (connected(i, j)) return;
int x = find(i), y = find(j);
cnt--;
if (rank[x] > rank[y]) {
p[y] = x;
size[x] += size[y];
} else {
p[x] = y;
size[y] += size[x];
if (rank[x] == rank[y]) rank[y]++;
}
}
};
int getMinConnectionChange(vector<int> connection) {
int nonCycle = 0;
int n = connection.size();
UF uf(n);
for(int i=0;i<n;i++) {
int to = connection[i] - 1;
if(to == i) nonCycle++;
else uf.join(i, to);
}
int components = uf.cnt;
int countCycle = uf.cnt - nonCycle;
int res = components - 1;
if(countCycle == components) res++; // all components have cycle
return res;
}
TL;DR: you can view this as looking for a minimal spanning arborescence problem.
More precisely, add a node for each server, and another one called "Terminate".
Make a complete graph (each node is linked to every other one) and set as cost 0 for the edges corresponding to your input, 1 for the other ones.
You can use for example Edmond's algorithm to solve this.
Related
As I was practicing C++ today, I came across a code which finds the shortest path in an unweighted graph using BFS and outputs the length of the path and the vertices it travelled through.
I attempted to change up this code by introducing user input.
First, the user has to input two integers, lets say N and M. N holds the number of vertices and M holds the number of edges.
The next M lines contain two integers, which indicates an undirected edge between two nodes.
I attempted to change the code in accordance to this, but I ran into a number of problems.
The first problem is that if I use gcc, the program will end after the for loop runs once in the main function.
However, if I use clang, the program runs fine. But there is an other issue and it relates to a very specific input.
The following input:
3 3
1 3
1 2
2 3
should return 1, but sometimes (specifically when I enter the input line by line) it returns the message: "Given source and destination are not connected". It is completely random.
The code is below:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
bool BFS(vector<int> adjList[], int source, int dest, int numOfVertices, int pred[], int dist[]);
void printShortestDistance(vector<int> adjList[], int s, int dest, int numOfVertices);
int main()
{
int numOfVertices, numOfEdges;
cin >> numOfVertices >> numOfEdges;
vector<int> adjList[numOfVertices];
if (2 <= numOfVertices && numOfVertices <= 1e5 && 1 <= numOfEdges && numOfEdges <= 1e5)
{
for (int i = 0; i < numOfEdges; i++)
{
int node1, node2;
cin >> node1 >> node2;
if ((1 <= node1) && (1 <= node2) && (node1 <= numOfVertices) && (node2 <= numOfVertices) && (node1 != node2))
{
adjList[node1].push_back(node2);
adjList[node2].push_back(node1);
}
else{ return EXIT_FAILURE; }
}
int source = 1;
int dest = numOfVertices;
printShortestDistance(adjList, source, dest, numOfVertices);
}
}
void printShortestDistance(vector<int> adjList[], int source, int dest, int numOfVertices)
{
int pred[numOfVertices];
int dist[numOfVertices];
if (BFS(adjList, source, dest, numOfVertices, pred, dist) == false)
{
cout << "Given source and destination are not connected";
return;
}
vector<int> path;
int crawl = dest;
path.push_back(crawl);
while (pred[crawl] != -1)
{
path.push_back(pred[crawl]);
crawl = pred[crawl];
}
cout << "Shortest path length is : " << dist[dest];
cout << "\nPath is::\n";
for (int i = path.size() - 1; i >= 0; i--)
cout << path[i] << " ";
}
bool BFS(vector<int> adjList[], int source, int dest, int numOfVertices, int pred[], int dist[])
{
list<int> queue;
bool visited[numOfVertices];
for (int i = 0; i < numOfVertices; i++)
{
visited[i] = false;
dist[i] = INT_MAX;
pred[i] = -1;
}
visited[source] = true;
dist[source] = 0;
queue.push_back(source);
while (!queue.empty())
{
int u = queue.front();
queue.pop_front();
for (int i = 0; i < adjList[u].size(); i++)
{
if (visited[adjList[u][i]] == false)
{
visited[adjList[u][i]] = true;
dist[adjList[u][i]] = dist[u] + 1;
pred[adjList[u][i]] = u;
queue.push_back(adjList[u][i]);
// We stop BFS when we find
// destination.
if (adjList[u][i] == dest)
return true;
}
}
}
return false;
}
"First, the user has to input two integers, lets say N and M. N holds the number of vertices and M holds the number of edges."
This is not a good idea. Humans are terrible at counting, while computers are pretty good at it. So do not make your users count the vertices and edges - they will often get it wrong and cause chaos.
Just input the start and ending nodes of an edge. If a node is already present in the data structure, connect it. If node not already present then add it and then connect it.
You will have much happier users!
I see that you have decided to store your graph in an adjacency list. This is a perfectly reasonable idea, but the snag is that it is quite a challenge to code. This seems to be your first attempt to code a graph theory problem, so I recommend that you use an adjacency matrix instead since it is much easier to code.
For small graphs the difference is insignificant, so you need only consider switching to the more complicate adjacency list when you are working with enormous graphs ( many thousands of nodes )
Remember that vectors in C++ are 0-based.
In your example, numOfVertices and numOfEdges are both 3, so node 3 will lead to out-of-bound access. Either change your input to 0-based node numbers or use node1-1 and node2-1.
See also
Vector going out of bounds without giving error
and the accepted answer.
I have problem in unweighted undirected graph the question ask if any path between two point in binary matrix, the user insert the matrix and the cell in matrix to test
bool BFS(vector<vector<int>>mat, Point src, Point dest,int n,int m)
{
if (!mat[src.x][src.y] || !mat[dest.x][dest.y])
return 0;
bool visited[n][m];
memset(visited, false, sizeof visited);
visited[src.x][src.y] = true;
queue<queueNode> q;
queueNode s = {src};
q.push(s);
while (!q.empty())
{
queueNode curr = q.front();
Point pt = curr.pt;
if (pt.x == dest.x && pt.y == dest.y)
return 1;
q.pop();
for (int i = 0; i < 8; i++)
{
int row = pt.x + rowNum[i];
int col = pt.y + colNum[i];
if (isValid(row, col,n,m) && mat[row][col] &&
!visited[row][col])
{
visited[row][col] = true;
queueNode Adjcell = {row, col};
q.push(Adjcell);
}
}
}
return 0;
}
In hacker rank some test not work properly because timeout.
Any one can help please?
Idea 1: if what you need is find out "if there is a path", then it is really not necessary to use a BFS algorithm. There is overhead, a lot of overhead in queue operation.
Just do a nested loop, for every A,B,C where [A,B] is connect, and [B,C] is connected, mark C[A,c] as well. Sort of like dynamic programming. And finally return the connection between the two input nodes.
BFS is only needed when there exists some choice that are better than others. It is not the case in this problem.
This is the link to the problem here
We have to tell the order in which the letters(like bricks) have to be laid in order for the wall to be stable. ie; for example if the input is
ZOAAMM
ZOAOMM
ZOOOOM
ZZZZOM
Then if I lay 'A's first then they will fall to the ground since in order to lay the letter 'A' I need to lay the letters 'Z' and 'O' first.
If I lay all O's first then they will also fall on the ground since some of the O's depend on the 'Z' below to be laid first.
The answer for the above is 'ZOMA' or 'ZOAM' both are valid answers. If it's not possible then we should print -1.
What I did in my code is that I first made a graph. For the example above, the graph will be O-> A since A depends on O. O->M since M also depends on O. Z->O since O depends on Z.
Then if there is a cycle in the graph then that means it is not possible to lay the wall so I print -1
If there is no cycle then the answer exists, and to get the answer I do a topological sort.
All the example test cases passed, but when I submit, I get a wrong answer. I don't know which test case could be failing. Can you let me know any case which my code is failing for please.
#include<bits/stdc++.h>
using namespace std;
vector<int>adj[26]; // adjacency list to store the graph
int n,m; // n rows each with a string of size m
string s[30];
bool vis[26]; //for topological sort
int visited[26];//for cycle detection
stack <char> mystack;
bool flag = 0;
void cycledfs(int node) // returns -1 if cycle detected
{
if(visited[node] == -1)
{
flag = 1;
return;
}
visited[node] = -1;
for(int i = 0; i < adj[node].size(); i++)
{
if(visited[adj[node][i]] != 1)
{
cycledfs(adj[node][i]);
}
}
visited[node] = 1;
}
void dfs(int node) // if cycle is not detected we do the topological sorting
{
if(!vis[node])
{
vis[node] = 1;
for(int i = 0; i < adj[node].size(); i++)
{
if(!vis[adj[node][i]])
dfs(adj[node][i]);
}
mystack.push(node+'A');
}
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
set <char> t; // set to store all the unique letters which are my vertices of the graph
for(int i = 0; i <n; i++)
{
cin>>s[i];
for(int j = 0; s[i][j]!='\0';j++)
t.insert(s[i][j]);
if(i)
{
for(int j = 0; s[i][j]!='\0';j++)//to check if the brick above me is different from me. Also making sure no duplicates in the graph.
{
auto it = find(adj[s[i][j]-'A'].begin(), adj[s[i][j]-'A'].end(), (s[i-1][j]-'A'));
if(((s[i][j]-'A') != (s[i-1][j]-'A')) &&(it==adj[s[i][j]-'A'].end()))
adj[s[i][j]-'A'].push_back((s[i-1][j]-'A'));
}
}
}
//initializing stuff
flag = 0;
memset(visited, 0, sizeof(visited));
memset(vis, 0, sizeof(vis));
for(char i: t)//CYCLE CHECKING
{
//cout<<(i-'A')<<"\n";
if(visited[i-'A'] == 0)
cycledfs(i-'A');
if(flag)
break;
}
if(flag)
cout<<"-1\n";
else //doing topological sort if no cycle
{
string result ="";
for(char x: t)
dfs(x-'A');
while(!mystack.empty())
{
char ans = mystack.top();
result.push_back(ans);
mystack.pop();
}
cout<<result<<"\n";
}
for(int i = 0; i < 26; i++) //clearing adj list
adj[i].clear();
}
}
There are N network nodes, labelled 1 to N.
Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, and w is the time it takes for a signal to travel from source to target.
Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1.
here is my code.. however it is giving wrong answer
class Solution {
public:
int networkDelayTime(vector <vector<int>> ×, int N, int K) {
vector<int> time(N + 1);
vector<int> visited(N + 1, 0);
vector < vector < pair < int, int >> > graph(N + 1);
for (int i = 0; i < times.size(); i++) {
graph[times[i][0]].push_back(make_pair(times[i][1], times[i][2]));
}
queue <pair<int, int>> q;
q.push(make_pair(K, 0));
visited[K] = 1;
time[K] = 0;
while (!q.empty()) {
int end = q.front().first;
int duration = q.front().second;
q.pop();
for (auto k:graph[end]) {
int first = k.first;
int second = k.second;
if (!visited[first]) {
q.push(make_pair(first, second));
time[first] = duration + second;
visited[first] = 1;
} else {
time[first] = min(time[first], (duration + second));
}
}
}
for (int i = 1; i <= N; i++) {
if (visited[i] == 0) {
return -1;
}
}
sort(time.begin(), time.end());
return time[N];
}
};
I am not able to figure out where I am wrong.
Thanks
This is a text-book application of Dijkstra's algorithm.
Given a node K, this algorithm will fill an array with the minimum distance from K to every other node, so the biggest value in this array will be the total time it takes for the signal to reach every other node.
You can't just use a BFS because it won't necessarily consider a shorter path to a node once it has already found any other path to that node. Dijkstra's algorithm is a modification of the BFS that deals with that. See this example, supposing the initial node is 1, the distances to the other nodes given by BFS and Dijkstra are different:
I have a structure defined like this:
struct Edge
{
int u, v; // vertices
Edge() { }
Edge(int u, int v)
{
this->u = u;
this->v = v;
}
};
and a class field defined like
vector<Edge> solution;
In one of the methods I'm creating new Edges and pushing them into the vector like this (a huge simplification of my real code, but the problem still exists):
solution.push_back(Edge(1, 2));
solution.push_back(Edge(3, 4));
solution.push_back(Edge(5, 6));
solution.push_back(Edge(7, 8));
solution.push_back(Edge(9, 10));
solution.push_back(Edge(11, 12));
solution.push_back(Edge(13, 14)); // adding 7th element; the problem occurs here
When the last push_back is executing, I'm getting an error window in Visual Studio's debug mode
[AppName] has triggered a breakpoint.
and the debugger goes to malloc.c, to the end of _heap_alloc function. Before this 7th line, the vector seems to work properly. I can see all the elements in the debugger. It seems that the vector has a problem reallocating itself (expanding its size).
What's interesting, if I put this before all the pushing back:
solution.reserve(7);
, the 7th edge is added properly. What's even more interesting, trying to reserve space for more than 22 elements also causes the mentioned error.
What am I doing wrong? How can I debug it? The rest of the application doesn't use so much memory, so I can't believe the heap is full.
More code, on request. It's a rather sloppy implementation of 2-approximation algorithm for Metric Travelling Salesman's Problem. It first creates a minimum spanning tree, then adds vertices (just indices) to the partialSolution vector in the DFS order.
void ApproxTSPSolver::Solve()
{
// creating a incidence matrix
SquareMatrix<float> graph(noOfPoints);
for (int r=0; r<noOfPoints; r++)
{
for (int c=0; c<noOfPoints; c++)
{
if (r == c)
graph.SetValue(r, c, MAX);
else
graph.SetValue(r, c, points[r].distance(points[c]));
}
}
// finding a minimum spanning tree
spanningTree = SquareMatrix<bool>(noOfPoints);
// zeroeing the matrix
for (int r=0; r<noOfPoints; r++)
for (int c=0; c<noOfPoints; c++)
spanningTree.SetValue(r, c, false);
bool* selected = new bool[noOfPoints];
memset(selected, 0, noOfPoints*sizeof(bool));
selected[0] = true; // the first point is initially selected
float min;
int minR, minC;
for (int i=0; i<noOfPoints - 1; i++)
{
min = MAX;
for (int r=0; r<noOfPoints; r++)
{
if (selected[r] == false)
continue;
for (int c=0; c<noOfPoints; c++)
{
if (selected[c] == false && graph.GetValue(r, c) < min)
{
min = graph.GetValue(r, c);
minR = r;
minC = c;
}
}
}
selected[minC] = true;
spanningTree.SetValue(minR, minC, true);
}
delete[] selected;
// traversing the tree
DFS(0);
minSol = 0.0f;
// rewriting the solution to the solver's solution field
for (int i=0; i<noOfPoints - 1; i++)
{
solution.push_back(Edge(partialSolution[i], partialSolution[i + 1]));
minSol += points[partialSolution[i]].distance(points[partialSolution[i + 1]]);
}
solution.push_back(Edge(partialSolution[noOfPoints - 1], partialSolution[0]));
minSol += points[partialSolution[noOfPoints - 1]].distance(points[partialSolution[0]]);
cout << endl << minSol << endl;
solved = true;
}
void ApproxTSPSolver::DFS(int vertex)
{
bool isPresent = std::find(partialSolution.begin(), partialSolution.end(), vertex)
!= partialSolution.end();
if (isPresent == false)
partialSolution.push_back(vertex); // if I comment out this line, the error doesn't occur
for (int i=0; i<spanningTree.GetSize(); i++)
{
if (spanningTree.GetValue(vertex, i) == true)
DFS(i);
}
}
class ApproxTSPSolver : public TSPSolver
{
vector<int> partialSolution;
SquareMatrix<bool> spanningTree;
void DFS(int vertex);
public:
void Solve() override;
};
from main.cpp:
TSPSolver* solver;
string inputFilePath, outputFilePath;
// parsing arguments
if (ArgParser::CmdOptionExists(argv, argv + argc, "/a"))
{
solver = new ApproxTSPSolver();
}
else if (ArgParser::CmdOptionExists(argv, argv + argc, "/b"))
{
solver = new BruteForceTSPSolver();
}
else
{
solver = new BranchAndBoundTSPSolver();
}
inputFilePath = ArgParser::GetCmdOption(argv, argv + argc, "/i");
outputFilePath = ArgParser::GetCmdOption(argv, argv + argc, "/s");
solver->LoadFromFile(inputFilePath);
Timer timer;
timer.start();
solver->Solve();
timer.stop();
cout << timer.getElapsedTime();
A part of TSPSolver.c:
TSPSolver::TSPSolver()
{
points = NULL;
solved = false;
}
TSPSolver::~TSPSolver()
{
if (points)
delete[] points;
}
void TSPSolver::LoadFromFile(string path)
{
ifstream input(path);
string line;
int nodeID;
float coordX, coordY;
bool coords = false;
minX = numeric_limits<float>::max();
maxX = numeric_limits<float>::min();
minY = numeric_limits<float>::max();
maxY = numeric_limits<float>::min();
while (input.good())
{
if (coords == false)
{
getline(input, line);
if (line == "NODE_COORD_SECTION")
{
coords = true;
}
else if (line.find("DIMENSION") != string::npos)
{
int colonPos = line.find_last_of(":");
noOfPoints = stoi(line.substr(colonPos + 1));
#ifdef _DEBUG
cout << noOfPoints << " points" << endl;
#endif
// allocating memory for this amount of points
points = new Point[noOfPoints];
}
}
else
{
input >> nodeID >> coordX >> coordY;
points[nodeID - 1].X = coordX;
points[nodeID - 1].Y = coordY;
minX = min(minX, coordX);
maxX = max(maxX, coordX);
minY = min(minY, coordY);
maxY = max(maxY, coordY);
if (nodeID == noOfPoints)
{
break;
}
}
}
input.close();
}
This is rather a comment then an answer, but the space is too limited.
If you are on windows, try Microsoft Application Verifier. It might detect wrong memory access.
Another way of detecting such access is to reseve empty char arrays initialized to 0.
Open your class where the vector is declared and declare a char array of let's say 64 chars before and after your vector and initialize them to 0!.
Then break into your vector code, where the error is generated and check the contents of those padding arrays. If they are filled, somebody writes more that it should.
A way to locate the "malicious" access (at least in VC++) is to set a data breakpoint writing in your padding arrays and check the callstack then.
You may be doing out-of-bounds accesses on points in various places, e.g. this one:
input >> nodeID >> coordX >> coordY;
points[nodeID - 1].X = coordX;
What if input failed, or the value is out of range?
I would suggest removing all uses of new and delete and []from your code; e.g. assuming points is int *points; then replace it with std::vector<int> points. Change all [] accesses to be .at() and catch exceptions. Disable copying on all classes that don't have correct copy semantics.
Then you can be more certain that it is not a memory allocation error, copying error, or out-of-bounds access (which are strong candidates for explaining your symptoms).
This would also fix the problem that TSPSolver currently does not have correct copy semantics.
It would be very useful to make a SSCCE. You mention that there is "a lot of input", try reducing the input as small as you can but still have the problem occur. An SSCCE can include input data so long as it is a manageable size that you can post. Currently you show too much code but not enough, as they say. The problem is still lurking somewhere you haven't posted yet.