Shortest path with one curved edge in an undirected Graph
Last Updated : 20 Nov, 2025
Given an undirected connected graph represented using an adjacency list adj[][][]. For each vertex i, the list adj[i] contains entries of the form {u, w1, w2}, where u is the neighboring vertex, w1 is the weight of the straight edge between i and u, and w2 is the weight of the curved edge between them. Thus, every pair of connected vertices has two parallel edges: one straight and one curved. We are also given two vertices a and b, and we need to determine the minimum cost required to travel from a to b under the constraint that:
we may use any number of straight edges
we may use at most one curved edge in the entire path.
If no such path exists that satisfies this restriction, we must return -1.
Output: 2 Explanation: We can follow the path 1->0->3. This gives a distance of 1+3 = 4 if we follow all straight paths. But we can take the curved path from 0-> 3, which costs 1. This will result in a cost of 1+1 = 2
Input: a = 0, b = 1, adj[][][] = [[[1, 4, 1]], [[0, 4, 1]]]
Output: 1 Explanation: Take the curved path from 0 to 1 which costs 1.
To solve this problem, our goal is to find the shortest path from node a to node b using any number of straight edges but at most one curved edge. Since all edge weights in the graph are positive, the best algorithm for finding shortest paths is Dijkstra’s algorithm, because it always picks the node with the smallest distance first and guarantees the minimum distance to every node it processes.
[Approach 1] Using Dijkstra with State Compression - O((V+E) log V) Time and O(V+E) Space
To handle the rule of using at most one curved edge, we track whether we have already used the curved edge while reaching each node. For this, we use a 2D distance array:
dist[node][0] -> shortest distance to node without using any curved edge
dist[node][1] -> shortest distance to node after using one curved edge
This allows us to maintain two separate states for every node and correctly compute paths that use zero or one curved edge.
We push states in the priority queue in the form (distance, node, usedCurve) and always process the smallest one first. When we explore edges from a node, we do two types of relaxations.
Curved edge - allowed only if usedCurve == 0, and it updates dist[next][1] since after using it we cannot use another curved edge.
Finally, the answer is min(dist[b][0], dist[b][1]), because the shortest path may or may not use a curved edge. If both values are infinite, no valid path exists.
C++
//Driver Code Starts#include<iostream>#include<vector>#include<queue>#include<array>usingnamespacestd;//Driver Code EndsintshortestPath(inta,intb,vector<vector<array<int,3>>>&adj){intn=adj.size();intINF=1e9;vector<vector<int>>dist(n,vector<int>(2,INF));// PQ stores state in form of {distance, node, usedCurve}usingState=array<int,3>;priority_queue<State,vector<State>,greater<State>>pq;// Start from 'a' without using any curved edgedist[a][0]=0;pq.push({0,a,0});while(!pq.empty()){autocur=pq.top();pq.pop();intd=cur[0];intnode=cur[1];intused=cur[2];if(d!=dist[node][used])continue;// Explore all neighbors of 'node'for(auto&it:adj[node]){intnxt=it[0];intw1=it[1];intw2=it[2];// Take straight edge - usedCurve remains sameif(dist[nxt][used]>d+w1){dist[nxt][used]=d+w1;pq.push({dist[nxt][used],nxt,used});}// Use curved edge (only if not used before)if(used==0){if(dist[nxt][1]>d+w2){dist[nxt][1]=d+w2;pq.push({dist[nxt][1],nxt,1});}}}}// Minimum of both possibilities (curved used or not used)intans=min(dist[b][0],dist[b][1]);returnans>=INF?-1:ans;}//Driver Code Startsintmain(){vector<vector<array<int,3>>>adj={{{1,1,4},{2,2,4},{3,3,1}},{{0,1,4},{3,6,5}},{{0,2,4}},{{0,3,1},{1,6,5}}};inta=1,b=3;cout<<shortestPath(a,b,adj);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.ArrayList;importjava.util.PriorityQueue;importjava.util.Comparator;publicclassGFG{//Driver Code EndsstaticintshortestPath(inta,intb,ArrayList<ArrayList<int[]>>adj){intn=adj.size();intINF=(int)1e9;// dist[node][usedCurve]int[][]dist=newint[n][2];for(inti=0;i<n;i++){dist[i][0]=INF;dist[i][1]=INF;}// PQ stores state in form of {distance, node, usedCurve}PriorityQueue<int[]>pq=newPriorityQueue<>(Comparator.comparingInt(o->o[0]));// Start from 'a' without using any curved edgedist[a][0]=0;pq.add(newint[]{0,a,0});while(!pq.isEmpty()){int[]cur=pq.poll();intd=cur[0];intnode=cur[1];intused=cur[2];if(d!=dist[node][used])continue;// Explore all neighbors of 'node'for(int[]it:adj.get(node)){intnxt=it[0];intw1=it[1];intw2=it[2];// Take straight edge - usedCurve remains sameif(dist[nxt][used]>d+w1){dist[nxt][used]=d+w1;pq.add(newint[]{dist[nxt][used],nxt,used});}// Use curved edge (only if not used before)if(used==0){if(dist[nxt][1]>d+w2){dist[nxt][1]=d+w2;pq.add(newint[]{dist[nxt][1],nxt,1});}}}}// Minimum of both possibilities (curved used or not used)intans=Math.min(dist[b][0],dist[b][1]);returnans>=INF?-1:ans;}//Driver Code StartsstaticvoidaddEdge(ArrayList<ArrayList<int[]>>adj,intx,inty,intw1,intw2){adj.get(x).add(newint[]{y,w1,w2});adj.get(y).add(newint[]{x,w1,w2});}publicstaticvoidmain(String[]args){intn=4;ArrayList<ArrayList<int[]>>adj=newArrayList<>();for(inti=0;i<n;i++)adj.add(newArrayList<>());addEdge(adj,0,1,1,4);addEdge(adj,0,2,2,4);addEdge(adj,0,3,3,1);addEdge(adj,1,3,6,5);inta=1,b=3;System.out.println(shortestPath(a,b,adj));}}//Driver Code Ends
Python
#Driver Code Startsimportheapq#Driver Code EndsdefshortestPath(a,b,adj):n=len(adj)INF=10**9# dist[node][usedCurveFlag]dist=[[INF,INF]for_inrange(n)]# PQ stores state in the form of (distance, node, usedCurve)pq=[]# Start from 'a' without using any curved edgedist[a][0]=0heapq.heappush(pq,(0,a,0))whilepq:d,node,used=heapq.heappop(pq)ifd!=dist[node][used]:continue# Explore all neighbors of 'node'fornxt,w1,w2inadj[node]:# Take straight edge - usedCurve remains sameifdist[nxt][used]>d+w1:dist[nxt][used]=d+w1heapq.heappush(pq,(dist[nxt][used],nxt,used))# Use curved edge (only if not used before)ifused==0:ifdist[nxt][1]>d+w2:dist[nxt][1]=d+w2heapq.heappush(pq,(dist[nxt][1],nxt,1))# Minimum of both possibilities (curved edge used or not)ans=min(dist[b][0],dist[b][1])return-1ifans>=INFelseans#Driver Code Startsif__name__=="__main__":adj=[[(1,1,4),(2,2,4),(3,3,1)],[(0,1,4),(3,6,5)],[(0,2,4)],[(0,3,1),(1,6,5)]]a=1b=3print(shortestPath(a,b,adj))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;publicclassMinHeap{privateList<int[]>heap=newList<int[]>();// each element = {distance, node, usedFlag}privatevoidSwap(inti,intj){vartemp=heap[i];heap[i]=heap[j];heap[j]=temp;}publicvoidPush(int[]item){heap.Add(item);intidx=heap.Count-1;// bubble upwhile(idx>0){intparent=(idx-1)/2;if(heap[parent][0]<=heap[idx][0])break;Swap(parent,idx);idx=parent;}}publicint[]Pop(){vartop=heap[0];varlast=heap[heap.Count-1];heap[0]=last;heap.RemoveAt(heap.Count-1);// bubble downintidx=0;while(true){intleft=idx*2+1;intright=idx*2+2;intsmallest=idx;if(left<heap.Count&&heap[left][0]<heap[smallest][0])smallest=left;if(right<heap.Count&&heap[right][0]<heap[smallest][0])smallest=right;if(smallest==idx)break;Swap(idx,smallest);idx=smallest;}returntop;}publicintCount{get{returnheap.Count;}}}publicclassGFG{//Driver Code EndspublicintshortestPath(inta,intb,List<List<int[]>>adj){intn=adj.Count;intINF=(int)1e9;// dist[node][usedCurveFlag]int[][]dist=newint[n][];for(inti=0;i<n;i++){dist[i]=newint[]{INF,INF};}// MinHeap stores in form of {distance, node, usedCurveFlag}MinHeappq=newMinHeap();// Start from a (without using curved edge)dist[a][0]=0;pq.Push(newint[]{0,a,0});while(pq.Count>0){int[]cur=pq.Pop();intd=cur[0];intnode=cur[1];intused=cur[2];if(d!=dist[node][used])continue;// Explore neighboursforeach(varitinadj[node]){intnxt=it[0];intw1=it[1];intw2=it[2];// Straight edgeif(dist[nxt][used]>d+w1){dist[nxt][used]=d+w1;pq.Push(newint[]{dist[nxt][used],nxt,used});}// Curved edge only onceif(used==0){if(dist[nxt][1]>d+w2){dist[nxt][1]=d+w2;pq.Push(newint[]{dist[nxt][1],nxt,1});}}}}intans=Math.Min(dist[b][0],dist[b][1]);returnans>=INF?-1:ans;}//Driver Code StartspublicstaticvoidaddEdge(List<List<int[]>>adj,intu,intv,intw1,intw2){adj[u].Add(newint[]{v,w1,w2});adj[v].Add(newint[]{u,w1,w2});}publicstaticvoidMain(){intV=4;List<List<int[]>>adj=newList<List<int[]>>();for(inti=0;i<V;i++){adj.Add(newList<int[]>());}addEdge(adj,0,1,1,4);addEdge(adj,0,2,2,4);addEdge(adj,0,3,3,1);addEdge(adj,1,3,6,5);inta=1,b=3;GFGobj=newGFG();Console.WriteLine(obj.shortestPath(a,b,adj));}}//Driver Code Ends
JavaScript
//Driver Code StartsclassMinHeap{constructor(){this.data=[];}push(item){this.data.push(item);this._up(this.data.length-1);}pop(){if(this.data.length===0)returnnull;lettop=this.data[0];letlast=this.data.pop();if(this.data.length>0){this.data[0]=last;this._down(0);}returntop;}_up(i){while(i>0){letp=Math.floor((i-1)/2);if(this.data[p][0]<=this.data[i][0])break;[this.data[p],this.data[i]]=[this.data[i],this.data[p]];i=p;}}_down(i){letn=this.data.length;while(true){letleft=2*i+1;letright=2*i+2;letsmallest=i;if(left<n&&this.data[left][0]<this.data[smallest][0])smallest=left;if(right<n&&this.data[right][0]<this.data[smallest][0])smallest=right;if(smallest===i)break;[this.data[smallest],this.data[i]]=[this.data[i],this.data[smallest]];i=smallest;}}empty(){returnthis.data.length===0;}}//Driver Code EndsfunctionshortestPath(a,b,adj){letn=adj.length;letINF=1e9;// dist[node][usedCurve]letdist=Array.from({length:n},()=>[INF,INF]);// PQ stores state in form of [distance, node, usedCurve]letpq=newMinHeap();// Start from 'a' without using any curved edgedist[a][0]=0;pq.push([0,a,0]);while(!pq.empty()){letcur=pq.pop();letd=cur[0];letnode=cur[1];letused=cur[2];if(d!==dist[node][used])continue;// Explore all neighbors of 'node'for(letitofadj[node]){letnxt=it[0];letw1=it[1];letw2=it[2];// Take straight edge - usedCurve remains sameif(dist[nxt][used]>d+w1){dist[nxt][used]=d+w1;pq.push([dist[nxt][used],nxt,used]);}// Use curved edge (only if not used before)if(used===0){if(dist[nxt][1]>d+w2){dist[nxt][1]=d+w2;pq.push([dist[nxt][1],nxt,1]);}}}}// Minimum of both possibilities (curved used or not used)letans=Math.min(dist[b][0],dist[b][1]);returnans>=INF?-1:ans;}//Driver Code Starts// Driver Codeletadj=[[[1,1,4],[2,2,4],[3,3,1]],[[0,1,4],[3,6,5]],[[0,2,4]],[[0,3,1],[1,6,5]]];leta=1,b=3;console.log(shortestPath(a,b,adj));//Driver Code Ends
Output
2
[Approach 2] UsingTwo-Dijkstra Method with Curved-Edge Relaxation - O((V+E) log V) Time and O(V+E) Space
We want to travel from a to b, and the main challenge is deciding where to use the one curved edge. To simplify this, we first compute the shortest straight-edge distances from a to every node and from b to every node. Once we have distances, we can easily try placing the curved edge between any two connected nodes.
We know the distance from a -> every node, and the distance from b -> every node. Now for each curved edge we can form two possible paths:
a -> u (straight) + u -> v (curved edge) + v -> b (straight)
a -> v (straight) + v -> u (curved edge) + u -> b (straight)
These two combinations represent both ways of using the curved edge exactly once. However, the problem states that we may use at most one curved edge. Therefore, we also need to consider the case where we do not use any curved edge at all. This normal straight-path distance from a -> b is simply da[b]. So, the final answer will be the minimum among all these cases.
C++
//Driver Code Starts#include<iostream>#include<vector>usingnamespacestd;//Driver Code Endsvector<int>dijkstra(intsrc,intn,vector<vector<array<int,3>>>&adj){intINF=1e9;vector<int>dist(n,INF);priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq;dist[src]=0;pq.push({0,src});while(!pq.empty()){auto[d,u]=pq.top();pq.pop();// Explore all neighbors of ufor(auto&x:adj[u]){intv=x[0];intstraight=x[1];// Relaxation stepif(dist[v]>d+straight){dist[v]=d+straight;pq.push({dist[v],v});}}}returndist;}// Function to find shortest path from a to b using at most one curved edgeintshortestPath(inta,intb,vector<vector<array<int,3>>>&adj){intn=adj.size();// Shortest distances using only straight edges //from source and destinationvector<int>da=dijkstra(a,n,adj);vector<int>db=dijkstra(b,n,adj);intans=da[b];// Check all curved edges to see if using one improves the answerfor(intu=0;u<n;u++){for(auto&x:adj[u]){intv=x[0];intcurved=x[2];// Two possible paths using the curved edgeans=min(ans,da[u]+curved+db[v]);ans=min(ans,da[v]+curved+db[u]);}}if(ans>=1e9)ans=-1;returnans;}//Driver Code Startsintmain(){inta=1,b=3;vector<vector<array<int,3>>>adj={{{1,1,4},{2,2,4},{3,3,1}},{{0,1,4},{3,6,5}},{{0,2,4}},{{0,3,1},{1,6,5}}};cout<<shortestPath(a,b,adj)<<endl;return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.ArrayList;importjava.util.PriorityQueue;importjava.util.Comparator;publicclassGFG{//Driver Code EndsstaticArrayList<Integer>dijkstra(intsrc,intn,ArrayList<ArrayList<int[]>>adj){intINF=(int)1e9;ArrayList<Integer>dist=newArrayList<>();for(inti=0;i<n;i++)dist.add(INF);PriorityQueue<int[]>pq=newPriorityQueue<>(Comparator.comparingInt(x->x[0]));dist.set(src,0);pq.offer(newint[]{0,src});while(!pq.isEmpty()){int[]cur=pq.poll();intd=cur[0];intu=cur[1];// Explore all neighbors of ufor(int[]x:adj.get(u)){intv=x[0];intstraight=x[1];// Relaxation stepif(dist.get(v)>d+straight){dist.set(v,d+straight);pq.offer(newint[]{dist.get(v),v});}}}returndist;}// Function to find shortest path from a to b using at most one curved edgestaticintshortestPath(inta,intb,ArrayList<ArrayList<int[]>>adj){intn=adj.size();// Shortest distances using only straight edges //from source and destinationArrayList<Integer>da=dijkstra(a,n,adj);ArrayList<Integer>db=dijkstra(b,n,adj);intans=da.get(b);// Check all curved edges to see if using one improves the answerfor(intu=0;u<n;u++){for(int[]x:adj.get(u)){intv=x[0];intcurved=x[2];// Two possible paths using the curved edgeans=Math.min(ans,da.get(u)+curved+db.get(v));ans=Math.min(ans,da.get(v)+curved+db.get(u));}}if(ans>=1000000000)ans=-1;returnans;}//Driver Code Starts// Add an undirected edgestaticvoidaddEdge(ArrayList<ArrayList<int[]>>adj,intu,intv,intstraight,intcurved){adj.get(u).add(newint[]{v,straight,curved});adj.get(v).add(newint[]{u,straight,curved});}publicstaticvoidmain(String[]args){inta=1,b=3;intV=4;ArrayList<ArrayList<int[]>>adj=newArrayList<>();for(inti=0;i<V;i++)adj.add(newArrayList<>());addEdge(adj,0,1,1,4);addEdge(adj,0,2,2,4);addEdge(adj,0,3,3,1);addEdge(adj,1,3,6,5);System.out.println(shortestPath(a,b,adj));}}//Driver Code Ends
Python
#Driver Code Startsimportheapq#Driver Code Endsdefdijkstra(src,n,adj):# Distance array initialized with INFdist=[10**9]*n# Min-heap priority queue: (distance_so_far, node)pq=[]dist[src]=0heapq.heappush(pq,(0,src))whilepq:d,u=heapq.heappop(pq)# Explore all neighbors of uforv,straight,curvedinadj[u]:# Relaxation stepifdist[v]>d+straight:dist[v]=d+straightheapq.heappush(pq,(dist[v],v))returndist# Function to find shortest path from a to b using at most one curved edgedefshortestPath(a,b,adj):n=len(adj)# Shortest distances using only straight edges # from source and destinationda=dijkstra(a,n,adj)db=dijkstra(b,n,adj)ans=da[b]# Check all curved edges to see if using one improves the answerforuinrange(n):forv,straight,curvedinadj[u]:# Two possible paths using the curved edgeans=min(ans,da[u]+curved+db[v])ans=min(ans,da[v]+curved+db[u])ifans>=10**9:return-1returnans#Driver Code Startsif__name__=="__main__":a=1b=3adj=[[(1,1,4),(2,2,4),(3,3,1)],[(0,1,4),(3,6,5)],[(0,2,4)],[(0,3,1),(1,6,5)]]print(shortestPath(a,b,adj))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;classMinHeap{privateList<int[]>heap=newList<int[]>();privatevoidSwap(inti,intj){vartemp=heap[i];heap[i]=heap[j];heap[j]=temp;}privateboolCompare(int[]a,int[]b){returna[0]<b[0];}publicvoidEnqueue(int[]val){heap.Add(val);inti=heap.Count-1;while(i>0){intparent=(i-1)/2;if(Compare(heap[i],heap[parent])){Swap(i,parent);i=parent;}elsebreak;}}publicint[]Dequeue(){if(heap.Count==0)returnnull;int[]top=heap[0];heap[0]=heap[heap.Count-1];heap.RemoveAt(heap.Count-1);inti=0;while(true){intleft=2*i+1;intright=2*i+2;intsmallest=i;if(left<heap.Count&&Compare(heap[left],heap[smallest]))smallest=left;if(right<heap.Count&&Compare(heap[right],heap[smallest]))smallest=right;if(smallest!=i){Swap(i,smallest);i=smallest;}elsebreak;}returntop;}publicintCount=>heap.Count;}classGFG{//Driver Code EndsstaticList<int>dijkstra(intsrc,intn,List<List<int[]>>adj){intINF=(int)1e9;List<int>dist=newList<int>();for(inti=0;i<n;i++)dist.Add(INF);// PriorityQueue stores {distance, node}MinHeappq=newMinHeap();dist[src]=0;pq.Enqueue(newint[]{0,src});while(pq.Count>0){varcur=pq.Dequeue();intd=cur[0];intu=cur[1];// Explore all neighbors of uforeach(varxinadj[u]){intv=x[0];intstraight=x[1];// Relaxation stepif(dist[v]>d+straight){dist[v]=d+straight;pq.Enqueue(newint[]{dist[v],v});}}}returndist;}// Function to find shortest path from a to b using at most one curved edgestaticintshortestPath(inta,intb,List<List<int[]>>adj){intn=adj.Count;// Shortest distances using only straight edges // from source and destinationList<int>da=dijkstra(a,n,adj);List<int>db=dijkstra(b,n,adj);intans=da[b];// Check all curved edges to see if using one improves the answerfor(intu=0;u<n;u++){foreach(varxinadj[u]){intv=x[0];intcurved=x[2];// Two possible paths using the curved edgeans=Math.Min(ans,da[u]+curved+db[v]);ans=Math.Min(ans,da[v]+curved+db[u]);}}if(ans>=1000000000)ans=-1;returnans;}//Driver Code Starts// Add an undirected edgestaticvoidaddEdge(List<List<int[]>>adj,intu,intv,intstraight,intcurved){adj[u].Add(newint[]{v,straight,curved});adj[v].Add(newint[]{u,straight,curved});}staticvoidMain(){inta=1,b=3;intV=4;List<List<int[]>>adj=newList<List<int[]>>();for(inti=0;i<V;i++)adj.Add(newList<int[]>());addEdge(adj,0,1,1,4);addEdge(adj,0,2,2,4);addEdge(adj,0,3,3,1);addEdge(adj,1,3,6,5);Console.WriteLine(shortestPath(a,b,adj));}}//Driver Code Ends
JavaScript
//Driver Code StartsclassMinHeap{constructor(){this.heap=[];}push(item){this.heap.push(item);this._siftUp(this.heap.length-1);}pop(){if(this.heap.length===0)returnnull;consttop=this.heap[0];constlast=this.heap.pop();if(this.heap.length>0){this.heap[0]=last;this._siftDown(0);}returntop;}isEmpty(){returnthis.heap.length===0;}_siftUp(i){while(i>0){letp=Math.floor((i-1)/2);if(this.heap[p][0]<=this.heap[i][0])break;[this.heap[p],this.heap[i]]=[this.heap[i],this.heap[p]];i=p;}}_siftDown(i){constn=this.heap.length;while(true){letleft=2*i+1;letright=2*i+2;letsmallest=i;if(left<n&&this.heap[left][0]<this.heap[smallest][0])smallest=left;if(right<n&&this.heap[right][0]<this.heap[smallest][0])smallest=right;if(smallest===i)break;[this.heap[i],this.heap[smallest]]=[this.heap[smallest],this.heap[i]];i=smallest;}}}//Driver Code Endsfunctiondijkstra(src,n,adj){letINF=1e9;letdist=Array(n).fill(INF);// Min-heap priority queue: [distance_so_far, node]letpq=newMinHeap();dist[src]=0;pq.push([0,src]);while(!pq.isEmpty()){let[d,u]=pq.pop();// Explore all neighbors of ufor(letxofadj[u]){letv=x[0];letstraight=x[1];// Relaxation stepif(dist[v]>d+straight){dist[v]=d+straight;pq.push([dist[v],v]);}}}returndist;}functionshortestPath(a,b,adj){letn=adj.length;// Shortest distances using only straight edges // from source and destinationletda=dijkstra(a,n,adj);letdb=dijkstra(b,n,adj);letans=da[b];// Check all curved edges to see if using one improves the answerfor(letu=0;u<n;u++){for(letxofadj[u]){letv=x[0];letcurved=x[2];// Two possible paths using the curved edgeans=Math.min(ans,da[u]+curved+db[v]);ans=Math.min(ans,da[v]+curved+db[u]);}}if(ans>=1e9)ans=-1;returnans;}//Driver Code Starts//Driver Codeleta=1,b=3;letadj=[[[1,1,4],[2,2,4],[3,3,1]],[[0,1,4],[3,6,5]],[[0,2,4]],[[0,3,1],[1,6,5]]];console.log(shortestPath(a,b,adj));//Driver Code Ends