let say edge (v,u) is the edge that the bellman ford algorithm faild in the n-th iteration - d(u) > d(v) + w(v,u). so we know that v,u is part of a negative cycle but the question is how do i detect the specific cycle?
In such a case, v, u may not be a part of a negative cycle. Instead, since a negative cycle exists, while relaxing edges in Bellman-Ford algorithm, we may visit u at a lower cost as the outgoing edges from a negative cycle may have led to u. In such a case, we have to track back from u until we know that we have stepped into such a cycle. What the code given in the answer points out, but fails to explain properly, is that by keeping a parents array and marking the parent of each node each time an edge is relaxed, we have guaranteed that the negative cycle can be visited by backtracking on the parents array until we find the same element. By keeping a visited array of booleans, we go back from u and keep marking all elements we backtrack using parents array as visited. When we visit the same node twice, we mark it as the end and start of our negative cycle and start inserting all the elements we visit while backtracking into our answer array, and upon coming back to the start/end of our negative cycle, we stop the loop. Even if u and v are part of the negative cycle, this approach works. Below is my code in C++ with comments provided(similar code as previous answer though):
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n, m;
cin>>n>>m;// n is number of vertices, m is number of edges
vector<vector<int>> edges(m, vector<int>(3));
for(int i=0;i<m;i++){
int a, b, c;
cin>>a>>b>>c;
if(a==b && c<0){//if there is a self-loop, no need to apply the algorithm
cout<<"YES"<<endl<<a<<" "<<a<<endl;
return 0;
}
a--;//for converting to 0-based indexing
b--;
edges[i][0]=a;
edges[i][1]=b;
edges[i][2]=c;
}
vector<int> dist(n, 1e18);//initialising distances
vector<int> parents(n, -1);
for(int i=0;i<n-1;i++){//applying bellman-ford
for(int j=0;j<m;j++){
if(dist[edges[j][1]]>dist[edges[j][0]]+edges[j][2]){
parents[edges[j][1]]=edges[j][0];//updating parents
dist[edges[j][1]]=dist[edges[j][0]]+edges[j][2];//relaxing edges
}
}
}
int j;
for(j=0;j<m;j++){
if(dist[edges[j][1]]>dist[edges[j][0]]+edges[j][2]){//u and v found
cout<<"cycle exists"<<endl;
int cur=edges[j][0];
vector<bool> vis(n, false);
while(!vis[cur]){//backtracking till we find the same element twice
vis[cur]=true;
cur=parents[cur];
}
cout<<cur+1;//that element must be a part of the negative cycle
int stop=cur;//marking it as the end element
vector<int> ans;
cur=parents[cur];
while(cur!=stop){//backtracking and inserting all the elements in the answer array
ans.push_back(cur+1);
cur=parents[cur];
}
for(int i=ans.size()-1;i>=0;i--){//printing the cycle elements
cout<<" "<<ans[i];
}
cout<<" "<<stop+1<<endl;//printing the end element
break;
}
}
if(j==m){//if j==m, no additional relaxations can be done
cout<<"no cycle exists"<<endl;
}
return 0;
}
Additionally, even though the Bellman-Ford algorithm is a single-source algorithm, we have modified it to effectively detect any negative cycle. This happens as all the nodes which have negative outgoing edges act as starting nodes in the first iteration in this algorithm. Since any negative cycle will always contain atleast one negative edge, this way of relaxation of edges helps us detect the cycles.