Finding the diameter of a tree using double DFS in C++ - c++

I'm attempting a problem on SPOJ where I'm supposed to find the longest path between any two nodes in a tree. The input consists of the number of testcases t, the number of nodes n, followed by n-1 edges given by "a b l", with a referring to node 1, b referring to node 2, and l referring to the length of the edge. I tried using the double dfs method, performing dfs on node 1 first to find the longest possible path beginning from node 1. Afterwards, I perform dfs on the node furthest away from node 1, finding the longest possible distance. Unfortunately, my code is wrong and I'm not sure why at all, and i'm hoping someone could help me out. Thanks in advance!
EDIT: Forgot to mention that I did manage to solve the problem using double BFS. I wanted to try using DFS to solve it as well because DFS is supposedly easier to implement than BFS, but using DFS gives me the wrong answer.
#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int>> adj[50001];
bool visited[50001] = {0};
int maxdist = -1, maxnode = -1;
void dfs(int node, int d)
{
visited[node] = 1;
if (d > maxdist)
{
maxdist = d;
maxnode = node;
}
for(auto u: adj[node])
{
if(!visited[u.first])
{
dfs(u.first, d+u.second);
}
}
return;
}
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
for (int i = 0; i < n-1; i++)
{
int a, b, l;
cin >> a >> b >> l;
adj[a].push_back(make_pair(b, l));
adj[b].push_back(make_pair(a, l));
}
dfs(1, 0);
for(int i = 1; i <= n; i++)
{
visited[i] = 0;
}
dfs(maxnode, 0);
cout << maxdist << endl;
}
}

Not sure if this will make it work, but you are sharing adj[] and visited[] between different test cases, make sure to clean them up before a new test case.

Related

Different results during Debug and Run in C++

I'm working on a algorithm problem, and it discribed as follows:
Suppose the deep learning model is a directed acyclic graph. If operator A depends on the output of operator B, then A can be calculated after the execution of B. If there is no dependency relation, then A can be executed in parallel. Given N nodes, the information of each node contains the execution time of the node and the list of the next nodes, and the shortest execution time of the neural network is calculated. The node index starts at 0.
and here is the input example:
7
A 10 1 2 3
B 9 4 5 6
C 22
D 20
E 19
F 18
G 21
Here is my solution:
#include <bits/stdc++.h>
using namespace std;
int dfs(int nodeTime, const vector<int>& nextNodes, vector<vector<int>> NN){
// Check whether the children of the current node have children
bool is_end = true;
for (int node : nextNodes) {
if (NN[node][1] != 0){
is_end = false;
break;
}
}
//The children of the current node have no children, find the maxTime
if (is_end) {
int maxTime = 0;
for (int node : nextNodes) {
maxTime = max(node, maxTime);
}
return nodeTime + maxTime;
}
//some children of the current node have children, keep doing dfs()
else{
int maxTime = 0;
for (int nodeIdx : nextNodes) {
if (NN[nodeIdx].size() != 1){
vector<int> next;
next.assign(NN[nodeIdx].begin() + 1, NN[nodeIdx].end());
maxTime = max(maxTime, dfs(NN[nodeIdx][0], next, NN));
}
else maxTime = max(maxTime, NN[nodeIdx][0]);
}
return maxTime + nodeTime;
}
}
int str_int(const string& s){
char c[10];
strcpy(c, s.c_str());
return atoi(c);
}
int main() {
// input stage
int n;
cin >> n;
vector<vector<int>> NN;
vector<int> temp;
vector<string> stemp;
string s;
for (int i = 0; i < n; ++i) {
stemp.clear();
temp.clear();
while (cin >> s){
stemp.push_back(s);
if (getchar() == '\n') break;
}
for (int j = 1; j < stemp.size(); ++j) {
temp.push_back(str_int(stemp[j]));
}
NN.push_back(temp);
}
vector<int> initialNextNodes; //Initialize the sequence of children of the starting node
initialNextNodes.assign(NN[0].begin() + 1, NN[0].end());
int res = dfs(NN[0][0], initialNextNodes, NN);
cout << res;
return 0;
}
The right output is 40, Debug mode gives the right answer, but Run mode gives the wrong answer, I can't figure out what went wrong. I looked up the common causes of this error, probably a pointer being used without space allocated, or a variable being used without an initial value assigned. But these do not seem to be the answer to my question, can anyone help me? I would be so grateful.

Find the Largest Distance between nodes of a Tree ? Can someone explain me the approach for this

Link to problem
InterviewBit solution to the problem
int Solution::solve(vector<int> &A)
{
vector<int> hgt(A.size(),0);
int ans=0,maxx=0;
for(int i=A.size()-1;i>0;i--)
{
ans=max(ans,hgt[A[i]]+hgt[i]+1);
hgt[A[i]]=max(hgt[i]+1,hgt[A[i]]);
}
return ans;
}
Can someone explain to me the above code as well as their approach where they said as follows :
Pick any node u.
Find the node which is farthest from u, call it x.
Find the node which is farthest from x, call it q.
The answer will be the length of a path from x to q.
Basically the problem is to find out the diameter of a tree.
Diameter of a Tree - It is the longest path between two nodes in a
tree.
Longest path will always occur between two leaf nodes.
Let's say, from given array you have made the tree.
Now you can use 2 DFS or BFS to do it.
Procedure:
Start BFS from a random node (let's say we run from root node) and
find out the farthest node from it. Let the farthest node be X. It is
clear that X will always be a leaf node.
Now if we start BFS from X and check the farthest node from it (like
we did previously), we will get the diameter of the tree.
Sample code:
#define MAX 40001
vector<int> adj[MAX];
int dist[MAX];
int totalNode;
pair<int, int> _bfs(int startingNode) {
for(int i=0; i <= totalNode; i++) {
dist[i] = 0;
}
dist[startingNode] = 1;
int maxDistance = 0, farthestNode;
queue<int> q;
q.push(startingNode);
while(!q.empty()) {
int currNode = q.front();
q.pop();
int sz = adj[currNode].size();
for(int i = 0; i < sz; i++) {
int nextNode = adj[currNode][i];
if(dist[nextNode] == 0) {
dist[nextNode] = dist[currNode] + 1;
q.push(nextNode);
if(dist[nextNode] > maxDistance) {
maxDistance = dist[nextNode], farthestNode = nextNode;
}
}
}
}
return {farthestNode, maxDistance};
}
int _getDiameter(int &rootNode) {
// Running the first BFS from the root node (as explained in the procedue 1)
pair<int, int> pii = _bfs(rootNode);
// Running the second BFS from the furthest node we've found after running first BFS (as explained in the procedure 2)
pair<int, int> pii2 = _bfs(pii.first);
return pii2.second;
}
int Solution::solve(vector<int> &A) {
totalNode = A.size();
int rootNode;
if(totalNode == 1) return 0;
if(totalNode == 2) return 1;
for(int i = 0; i < totalNode; i++) adj[i].clear();
for(int i = 0; i < totalNode; i++) {
int n = A[i];
if(n == -1) rootNode = i;
else adj[i].push_back(n), adj[n].push_back(i);
}
return _getDiameter(rootNode) - 1;
}
Reference:
Diameter of a tree using DFS
Finding Diameter of a Tree using DFS with proof

BFS using deque

Really having trouble figuring out how to fix my code. I know there are obviously errors since it isn't running, but I'm unsure of what exactly they are, or how to go about fixing them. Any help/insight would be extremely appreciated. Thank you!!
struct vertices
{
int value;
int parent;
int visited;
int distance;
};
int BFS(vertices *v, int **adj_matrix, int num_nodes)
{
int target;
int cur_v = 0;
bool found = false;
int steps = 0;
cin >> target >> num_nodes;
adj_matrix [num_nodes][num_nodes];
deque<int> q;
for(int i = 0; i < num_nodes; i++)
{
v[i].visited = 0;
v[i].distance = INFINITY;
v[i].parent = 0;
v[1].visited = 1;
v[i].distance = 0;
q.push_front(v[1].value);
while(!q.empty())
{
cur_v = q.front();
q.pop_back();
v[cur_v].visited = 1;
for(int n=0; n< num_nodes; n++)
{
if(adj_matrix[cur_v][i] == n)
{
if(v[n].visited == 0)
{
v[n].visited = 1;
v[n].distance = ((v[cur_v].distance)+1);
v[n].parent = cur_v;
q.push_front(v[n].value);
steps ++;
}
}
}
}
}
return steps;
}
int main()
{
int target;
int num_nodes;
cin >> target;
cin >> num_nodes;
vertices *v = new vertices[num_nodes];
int **adj_matrix [num_nodes][num_nodes];
for(int i=0; i < num_nodes; ++i)
{
int node;
int value;
cin >> node >> value;
int num_edges;
cin >> num_edges;
for(int j=0; j<num_edges;++j)
{
int other_node;
cin >> other_node;
**adj_matrix[node][other_node] = 1;
**adj_matrix[other_node][node] = 1;
}
}
}
The first obvious error is that you are using the wrong data
structure. When you are implementing known concepts like matrix,
BFS, you need to spend a good amount of time to think about how to
implement inputs , outputs, data structures from the algorithm.
Some people like to use std::vector<std::vector<int>> for matrices. I almost always uses std::vector<int> for matrix data.
Second error is that you are mutating the algorithm. That thing is not BFS. It is not obvious at first.
You call multiple instance of BFS inside you BFS implementation. Using the same input vertices, whose elements are being modified. At the next loop, you dont start with a clean state.
You are flattening the algorithm, removing abstractions. What if you now have to use a adjency list? You have to modify the whole thing.
Third obvious error is coding style. You didnt do such a bad job because the vertices structure is self explanatory
I had to highlight all instances of cur_vto realizes that it stands for current vertex.
Using n for a counter is not a great idea.
Taking input inside the algorithm implementation is a big nono.

Trouble with Dijkstra , finding all minimum paths

We have a problem here, we're trying to find all the shortest paths in graph from one node to another. We have already implemented dijkstra but we really dont know how to find them all.
Do we have to use BFS?
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
typedef pair <int, int> dist_node;
typedef pair <int, int> edge;
const int MAXN = 10000;
const int INF = 1 << 30;
vector <edge> g[MAXN];
int d[MAXN];
int p[MAXN];
int dijkstra(int s, int n,int t){
for (int i = 0; i <= n; ++i){
d[i] = INF; p[i] = -1;
}
priority_queue < dist_node, vector <dist_node>,greater<dist_node> > q;
d[s] = 0;
q.push(dist_node(0, s));
while (!q.empty()){
int dist = q.top().first;
int cur = q.top().second;
q.pop();
if (dist > d[cur]) continue;
for (int i = 0; i < g[cur].size(); ++i){
int next = g[cur][i].first;
int w_extra = g[cur][i].second;
if (d[cur] + w_extra < d[next]){
d[next] = d[cur] + w_extra;
p[next] = cur;
q.push(dist_node(d[next], next));
}
}
}
return d[t];
}
vector <int> findpath (int t){
vector <int> path;
int cur=t;
while(cur != -1){
path.push_back(cur);
cur = p[cur];
}
reverse(path.begin(), path.end());
return path;
}
This is our code, we believe we have to modify it but we really don't know where.
Currently, you are only saving/retrieving one of the shortest paths that you happen to find. Consider this example:
4 nodes
0 -> 1
0 -> 2
1 -> 3
2 -> 3
It becomes clear that you cannot have a single p[] value for each position, as in fact the 4th node (3) has 2 previous valid nodes: 1 and 2.
You could thus replace it with a vector<int> p[MAXN]; and work as follows:
if (d[cur] + w_extra < d[next]){
d[next] = d[cur] + w_extra;
p[next].clear();
p[next].push_back(cur);
q.push(dist_node(d[next], next));
}
else if(d[cur] + w_extra == d[next]){
p[next].push_back(cur); // a new shortest way of hitting this same node
}
You will also need to update your findpath() function as it will need to deal with "branches" resulting in several multiple paths, possibly an exponentially huge amount of paths depending on the graph. If you just need to print the paths, you could do something like this:
int answer[MAXN];
void findpath (int t, int depth){
if(t == -1){ // we reached the initial node of one shortest path
for(int i = depth-1; i >= 0; --i){
printf("%d ", answer[i]);
}
printf("%d\n", last_node); // the target end node of the search
return;
}
for(int i = p[t].size()-1; i >= 0; --i){
answer[depth] = p[t][i];
findpath(p[t][i], depth+1);
}
}
Note you'll need to do p[s].push_back(-1) at the beginning of your dijkstra, besides clearing this vector array between cases.

Dijkstra's algorithm question

In the code below:
#define MAX_VERTICES 260000
#include <fstream>
#include <vector>
#include <queue>
#define endl '\n'
using namespace std;
struct edge {
int dest;
int length;
};
bool operator< (edge e1, edge e2) {
return e1.length > e2.length;
}
int C, P, P0, P1, P2;
vector<edge> edges[MAX_VERTICES];
int best1[MAX_VERTICES];
int best2[MAX_VERTICES];
void dijkstra (int start, int* best) {
for (int i = 0; i < P; i++) best[i] = -1;
best[start] = 0;
priority_queue<edge> pq;
edge first = { start, 0 };
pq.push(first);
while (!pq.empty()) {
edge next = pq.top();
pq.pop();
if (next.length != best[next.dest]) continue;
for (vector<edge>::iterator i = edges[next.dest].begin(); i != edges[next.dest].end(); i++) {
if (best[i->dest] == -1 || next.length + i->length < best[i->dest]) {
best[i->dest] = next.length + i->length;
edge e = { i->dest, next.length+i->length };
pq.push(e);
}
}
}
}
int main () {
ifstream inp("apple.in");
ofstream outp("apple.out");
inp >> C >> P >> P0 >> P1 >> P2;
P0--, P1--, P2--;
for (int i = 0; i < C; i++) {
int a, b;
int l;
inp >> a >> b >> l;
a--, b--;
edge e = { b, l };
edges[a].push_back(e);
e.dest = a;
edges[b].push_back(e);
}
dijkstra (P1, best1); // find shortest distances from P1 to other nodes
dijkstra (P2, best2); // find shortest distances from P2 to other nodes
int ans = best1[P0]+best1[P2]; // path: PB->...->PA1->...->PA2
if (best2[P0]+best2[P1] < ans)
ans = best2[P0]+best2[P1]; // path: PB->...->PA2->...->PA1
outp << ans << endl;
return 0;
}
What is this: if (next.length != best[next.dest]) continue; used for? Is it to avoid us situations where going through the loop will give us the same answer that we already have?
Thanks!
That line is a way to handle the fact that c++'s priority_queue does not have a decrease_key function.
That is, when you do pq.push(e) and there is already an edge with the same destination in the heap you would prefer to decrease the key of the edge already in the heap. This is not easily done with c++'s priority_queue and so a simple way to handle it is to allow multiple edges in the heap corresponding to the same destination and ignoring all but the first (for each dest) that you pop from the heap.
Note that this changes the complexity from O(ElogV) to O(ElogE).
I guess you are contemplating the case where your priority_queue contains 2 times the same edge, but each one with a different "length".
This could happen if you push edge X which has a length of Y, and afterwards push edge X again, but this time it has a length < Y. That is why, if the length of that edge, isn't the lowest you've found for that edge so far, you ommit it in that loop's iteration.