Given an integer n representing an n × n Snake and Ladder board with cells numbered from 1 to n2, find the minimum number of dice throws required to reach cell n2 starting from cell 1. Two arrays of even length are provided:
lad[], where each pair (lad[2*i], lad[2*i + 1]) represents the start and end cells of a ladder.
sn[], where each pair (sn[2*i], sn[2*i + 1])represents the start and end cells of a snake.
Whenever a player lands on the starting cell of a snake or ladder, they must immediately move to its corresponding destination cell.
In a single dice throw, the player can move forward by any number of cells from 1 to 6. If it is not possible to reach the last cell, return -1.
Example:
Input: n = 6, lad[] = [3, 22, 5, 8, 11, 35, 20, 32], sn[] = [17, 4, 19, 7, 34, 1, 21, 9] Output: 3 Explanation: Move from 1 to 5 and take the ladder to 8. Then move to 11 and take the ladder to 35. Finally, move from 35 to 36. Therefore, the minimum number of dice throws required is 3.
Input: n = 3, lad[] = [2, 8], sn[] = [7, 3] Output: 2 Explanation: Move from 1 to 2 and take the ladder to 8. Then move from 8 to 9, which is the destination. Therefore, the minimum number of dice throws required is 2.
[Naive Approach] Using DFS with Pruning - O(6^(n^2)) Time and O(n^2) Space
The idea is to use DFS to try all possible dice outcomes from 1 to 6 at each cell. If a snake or ladder is encountered, we immediately move to its destination. To reduce unnecessary exploration, we keep track of the minimum throws needed to reach each cell and prune paths that cannot lead to a better answer.
Store all snakes and ladders in a move[] array.
Start DFS from cell 1 with 0 throws.
Skip paths that are already worse than the current best answer.
Skip a cell if it was reached earlier with fewer throws.
Try all possible dice outcomes from 1 to 6.
Apply any snake or ladder jump and continue the DFS.
Update the answer when the destination is reached.
Return the minimum number of throws found.
C++
#include<bits/stdc++.h>usingnamespacestd;intres;// DFS with pruningvoiddfs(intcurrPos,intmovesMade,vector<int>&move,unordered_map<int,int>&best,intm){// Prune if already worse than best answerif(movesMade>=res)return;// Prune if already visited in better or equal wayif(best.count(currPos)&&best[currPos]<=movesMade)return;best[currPos]=movesMade;// Destination reachedif(currPos==m){res=min(res,movesMade);return;}// Try all dice outcomesfor(intdice=1;dice<=6&&currPos+dice<=m;dice++){intnxt=currPos+dice;// Apply snake or ladderif(move[nxt]!=-1)nxt=move[nxt];dfs(nxt,movesMade+1,move,best,m);}}// Function to compute minimum throwsintminThrows(intn,vector<int>&lad,vector<int>&sn){intm=n*n;vector<int>move(m+1,-1);// Store laddersfor(inti=0;i+1<(int)lad.size();i+=2)move[lad[i]]=lad[i+1];// Store snakesfor(inti=0;i+1<(int)sn.size();i+=2)move[sn[i]]=sn[i+1];unordered_map<int,int>best;res=INT_MAX;dfs(1,0,move,best,m);return(res==INT_MAX)?-1:res;}intmain(){intn=6;vector<int>lad={3,22,5,8,11,35,20,32};vector<int>sn={17,4,19,7,34,1,21,9};cout<<minThrows(n,lad,sn)<<endl;return0;}
Java
importjava.util.Arrays;classGFG{staticintres;// DFS with pruningstaticvoiddfs(intcurrPos,intmovesMade,int[]move,int[]best,intm){// Prune if already worse than best answerif(movesMade>=res)return;// Prune if already reached this cell in fewer throwsif(movesMade>=best[currPos])return;best[currPos]=movesMade;// Destination reachedif(currPos==m){res=Math.min(res,movesMade);return;}// Try all dice outcomesfor(intdice=1;dice<=6&&currPos+dice<=m;dice++){intnxt=currPos+dice;// Apply snake or ladderif(move[nxt]!=-1)nxt=move[nxt];dfs(nxt,movesMade+1,move,best,m);}}// Function to compute minimum throwsstaticintminThrows(intn,int[]lad,int[]sn){intm=n*n;int[]move=newint[m+1];Arrays.fill(move,-1);// Store laddersfor(inti=0;i+1<lad.length;i+=2)move[lad[i]]=lad[i+1];// Store snakesfor(inti=0;i+1<sn.length;i+=2)move[sn[i]]=sn[i+1];int[]best=newint[m+1];Arrays.fill(best,Integer.MAX_VALUE);res=Integer.MAX_VALUE;dfs(1,0,move,best,m);return(res==Integer.MAX_VALUE)?-1:res;}publicstaticvoidmain(String[]args){intn=6;int[]lad={3,22,5,8,11,35,20,32};int[]sn={17,4,19,7,34,1,21,9};System.out.println(minThrows(n,lad,sn));}}
Python
res=float('inf')# DFS with pruningdefdfs(currPos,movesMade,move,best,m):globalres# Prune if already worse than best answerifmovesMade>=res:return# Prune if already visited in better or equal wayifcurrPosinbestandbest[currPos]<=movesMade:returnbest[currPos]=movesMade# Destination reachedifcurrPos==m:res=min(res,movesMade)return# Try all dice outcomesfordiceinrange(1,7):ifcurrPos+dice>m:continuenxt=currPos+dice# Apply snake or ladderifmove[nxt]!=-1:nxt=move[nxt]dfs(nxt,movesMade+1,move,best,m)# Function to compute minimum throwsdefminThrows(n,lad,sn):globalresm=n*nmove=[-1]*(m+1)# Store laddersforiinrange(0,len(lad),2):move[lad[i]]=lad[i+1]# Store snakesforiinrange(0,len(sn),2):move[sn[i]]=sn[i+1]best={}res=float('inf')dfs(1,0,move,best,m)return-1ifres==float('inf')elseresif__name__=="__main__":n=6lad=[3,22,5,8,11,35,20,32]sn=[17,4,19,7,34,1,21,9]print(minThrows(n,lad,sn))
C#
usingSystem;usingSystem.Collections.Generic;classGFG{staticintres;// DFS with pruningstaticvoiddfs(intcurrPos,intmovesMade,int[]move,Dictionary<int,int>best,intm){// Prune if already worse than best answerif(movesMade>=res)return;// Prune if already visited in better or equal wayif(best.ContainsKey(currPos)&&best[currPos]<=movesMade)return;best[currPos]=movesMade;// Destination reachedif(currPos==m){res=Math.Min(res,movesMade);return;}// Try all dice outcomesfor(intdice=1;dice<=6&&currPos+dice<=m;dice++){intnxt=currPos+dice;// Apply snake or ladderif(move[nxt]!=-1)nxt=move[nxt];dfs(nxt,movesMade+1,move,best,m);}}// Function to compute minimum throwspublicstaticintminThrows(intn,int[]lad,int[]sn){intm=n*n;int[]move=newint[m+1];for(inti=0;i<=m;i++)move[i]=-1;// Store laddersfor(inti=0;i+1<lad.Length;i+=2)move[lad[i]]=lad[i+1];// Store snakesfor(inti=0;i+1<sn.Length;i+=2)move[sn[i]]=sn[i+1];Dictionary<int,int>best=newDictionary<int,int>();res=int.MaxValue;dfs(1,0,move,best,m);return(res==int.MaxValue)?-1:res;}staticvoidMain(){intn=6;int[]lad={3,22,5,8,11,35,20,32};int[]sn={17,4,19,7,34,1,21,9};Console.WriteLine(minThrows(n,lad,sn));}}
JavaScript
// DFS with pruningfunctiondfs(currPos,movesMade,move,best,m,resObj){// Prune if already worse than best answerif(movesMade>=resObj.res)return;// Prune if already visited in better or equal wayif(best.has(currPos)&&best.get(currPos)<=movesMade)return;best.set(currPos,movesMade);// Destination reachedif(currPos===m){resObj.res=Math.min(resObj.res,movesMade);return;}// Try all dice outcomesfor(letdice=1;dice<=6&&currPos+dice<=m;dice++){letnxt=currPos+dice;// Apply snake or ladderif(move[nxt]!==-1)nxt=move[nxt];dfs(nxt,movesMade+1,move,best,m,resObj);}}// Function to compute minimum throwsfunctionminThrows(n,lad,sn){letm=n*n;letmove=newArray(m+1).fill(-1);// Store laddersfor(leti=0;i+1<lad.length;i+=2)move[lad[i]]=lad[i+1];// Store snakesfor(leti=0;i+1<sn.length;i+=2)move[sn[i]]=sn[i+1];letbest=newMap();// replace global res with object wrapperletresObj={res:Infinity};dfs(1,0,move,best,m,resObj);return(resObj.res===Infinity)?-1:resObj.res;}// Driver codeletn=6;letlad=[3,22,5,8,11,35,20,32];letsn=[17,4,19,7,34,1,21,9];console.log(minThrows(n,lad,sn));
Output
3
[Expected Approach] Using Breadth First Search (BFS) - O(n^2) Time and O(n^2) Space
The idea is to explore the board level by level using Breadth First Search (BFS). From each cell, we try all possible dice outcomes from 1 to 6. If a move lands on a snake or ladder, we immediately jump to its destination cell. Since every dice throw has the same cost, the first time BFS reaches the last cell, it guarantees the minimum number of dice throws.
Store all snakes and ladders in a moves[] array.
Start BFS from cell 1.
For each cell, try all dice outcomes from 1 to 6.
Apply any snake or ladder jump to the next cell.
Add unvisited cells to the queue.
Continue until the destination cell is reached.
Return the minimum number of dice throws.
C++
#include<iostream>#include<queue>#include<vector>usingnamespacestd;intminThrows(intn,vector<int>&lad,vector<int>&sn){vector<int>moves(n*n+1,-1);vector<bool>vis(n*n+1,false);// Store all snakes and ladders.for(inti=0;i<(int)lad.size();i+=2){moves[lad[i]]=lad[i+1];}for(inti=0;i<(int)sn.size();i+=2){moves[sn[i]]=sn[i+1];}queue<pair<int,int>>q;q.push({1,0});vis[1]=true;pair<int,int>cur;while(!q.empty()){cur=q.front();q.pop();intpos=cur.first;intdist=cur.second;if(pos==n*n){returndist;}// Try all possible dice outcomes.for(intnxt=pos+1;nxt<=pos+6&&nxt<=n*n;nxt++){if(!vis[nxt]){vis[nxt]=true;intdest=(moves[nxt]==-1)?nxt:moves[nxt];q.push({dest,dist+1});}}}return-1;}intmain(){intn=6;vector<int>lad={3,22,5,8,11,35,20,32};vector<int>sn={17,4,19,7,34,1,21,9};cout<<minThrows(n,lad,sn)<<endl;return0;}
Java
importjava.util.Arrays;importjava.util.LinkedList;importjava.util.Queue;classGFG{staticintminThrows(intn,int[]lad,int[]sn){int[]moves=newint[n*n+1];Arrays.fill(moves,-1);boolean[]vis=newboolean[n*n+1];// Store all snakes and ladders.for(inti=0;i<lad.length;i+=2){moves[lad[i]]=lad[i+1];}for(inti=0;i<sn.length;i+=2){moves[sn[i]]=sn[i+1];}Queue<int[]>q=newLinkedList<>();q.offer(newint[]{1,0});vis[1]=true;int[]cur;while(!q.isEmpty()){cur=q.poll();intpos=cur[0];intdist=cur[1];if(pos==n*n){returndist;}// Try all possible dice outcomes.for(intnxt=pos+1;nxt<=pos+6&&nxt<=n*n;nxt++){if(!vis[nxt]){vis[nxt]=true;intdest=(moves[nxt]==-1)?nxt:moves[nxt];q.offer(newint[]{dest,dist+1});}}}return-1;}publicstaticvoidmain(String[]args){intn=6;int[]lad={3,22,5,8,11,35,20,32};int[]sn={17,4,19,7,34,1,21,9};System.out.println(minThrows(n,lad,sn));}}
Python
fromcollectionsimportdequedefminThrows(n,lad,sn):moves=[-1]*(n*n+1)vis=[False]*(n*n+1)# Store all snakes and ladders.foriinrange(0,len(lad),2):moves[lad[i]]=lad[i+1]foriinrange(0,len(sn),2):moves[sn[i]]=sn[i+1]q=deque()q.append((1,0))vis[1]=Truewhileq:cur=q.popleft()pos=cur[0]dist=cur[1]ifpos==n*n:returndist# Try all possible dice outcomes.fornxtinrange(pos+1,min(pos+6,n*n)+1):ifnotvis[nxt]:vis[nxt]=Truedest=nxtifmoves[nxt]==-1elsemoves[nxt]q.append((dest,dist+1))return-1if__name__=="__main__":n=6lad=[3,22,5,8,11,35,20,32]sn=[17,4,19,7,34,1,21,9]print(minThrows(n,lad,sn))
C#
usingSystem;usingSystem.Collections.Generic;classGFG{staticintminThrows(intn,int[]lad,int[]sn){int[]moves=newint[n*n+1];Array.Fill(moves,-1);bool[]vis=newbool[n*n+1];// Store all snakes and ladders.for(inti=0;i<lad.Length;i+=2){moves[lad[i]]=lad[i+1];}for(inti=0;i<sn.Length;i+=2){moves[sn[i]]=sn[i+1];}Queue<(int,int)>q=newQueue<(int,int)>();q.Enqueue((1,0));vis[1]=true;(int,int)cur;while(q.Count>0){cur=q.Dequeue();intpos=cur.Item1;intdist=cur.Item2;if(pos==n*n){returndist;}// Try all possible dice outcomes.for(intnxt=pos+1;nxt<=pos+6&&nxt<=n*n;nxt++){if(!vis[nxt]){vis[nxt]=true;intdest=(moves[nxt]==-1)?nxt:moves[nxt];q.Enqueue((dest,dist+1));}}}return-1;}staticvoidMain(){intn=6;int[]lad={3,22,5,8,11,35,20,32};int[]sn={17,4,19,7,34,1,21,9};Console.WriteLine(minThrows(n,lad,sn));}}
JavaScript
functionminThrows(n,lad,sn){letmoves=newArray(n*n+1).fill(-1);letvis=newArray(n*n+1).fill(false);// Store all snakes and ladders.for(leti=0;i<lad.length;i+=2){moves[lad[i]]=lad[i+1];}for(leti=0;i<sn.length;i+=2){moves[sn[i]]=sn[i+1];}letq=[];q.push([1,0]);vis[1]=true;while(q.length>0){letcur=q.shift();letpos=cur[0];letdist=cur[1];if(pos===n*n){returndist;}// Try all possible dice outcomes.for(letnxt=pos+1;nxt<=pos+6&&nxt<=n*n;nxt++){if(!vis[nxt]){vis[nxt]=true;letdest=(moves[nxt]===-1)?nxt:moves[nxt];q.push([dest,dist+1]);}}}return-1;}functionmain(){letn=6;letlad=[3,22,5,8,11,35,20,32];letsn=[17,4,19,7,34,1,21,9];console.log(minThrows(n,lad,sn));}main();