Puzzle for Facebook example: Hanoi Towers

Here's a question from a sample survey for hiring Facebook.

There are pegs K. Each pin can hold the discs in decreasing order of radius when viewed from the bottom up. There are N disks with a radius of 1 to N; Given the initial configuration of the bindings and the final configuration of the pegs, output the moves necessary for the conversion from the initial to the final configuration. You must do the conversion in a minimum number of moves.

Moving consists of selecting the topmost disk of any of the pins and installing it on top of any other pin. At any time, it is necessary to preserve the property of reducing the radius of all pegs.

Limitations:

1 <= N <= 8

3 <= K <= 5

Input format:

Nk

The second line contains N integers. Each integer is in the range from 1 to K, where the ith integer denotes the binding to which the disk of radius i is in the initial configuration.

The third line indicates the final configuration in a format similar to the original configuration.

Output format:

The first line contains M - the minimum number of moves necessary to complete the conversion.

The next M lines describe the movement, using the snap number to select and the snap number to place. If there are several solutions, just print any of them. You can assume that there is always a solution with less than 7 moves, and the initial configuration will not be the same as the final one.

Input Example # 00:

2 3

eleven

2 2

Output Example # 00:

3

13

12

3 2

Input Example # 01:

6 4

4 2 4 3 1 1

1 1 1 1 1 1

Output Example # 01:

5

3 1

4 3

4 1

2 1

3 1

There is no harm in discussing a solution to this problem, since it is a sample problem.

The solution to the classic Hanoi Tower problem is really easy to code:

void hanoi(char s, char i, char d, int n) { if(n>0) { hanoi(s, d, i, n-1); cout<<s<<":"<<d<<endl; hanoi(i, s, d, n-1); } } 

The foregoing can also be extended to the general tower "k" of Hanoi cave. But this knowledge is not entirely useful for developing a solution to this puzzle. Any suggestions on how to approach these and similar problems in the future?

+6
source share
6 answers

Here, my dynamic programming solution, which finds the optimal sequence of moves in no more than stages O (K ^ N), works for a second for K = 5, N = 8. I hard-coded the input due to laziness.

This is a BFS through a state space that never visits the same state twice. Then it gets the actual path, returning from end to beginning (this part is linear with the length of the optimal sequence). In principle, this is the “shortest path through the maze” algorithm, but the “maze” is the space of problem states, the initial “location” is the initial state, and the final “location” is the desired state.

Many of these problems can be solved in this way, as long as you can determine the space of the final state, the “distance” between the two states, which your goal is minimized, and a way to calculate from which states you can go from the Current state.

For example, the problem of "missionaries and cannibals" with an arbitrary number of each can be solved using the same algorithm.

In addition, if you need “all optimal solutions” instead of “any optimal solutions”, it is easy to change the algorithm to provide them.

 class Program { static int N = 8; static int K = 5; static List<int> StartState = new List<int> { 3, 3, 2, 1, 4, 1, 5, 2 }; static List<int> EndState = new List<int> { 1, 4, 2, 2, 3, 4, 4, 3 }; static LinkedList<int> StateQueue = new LinkedList<int>(); static int[] MovesToState = new int[(int)Math.Pow(K, N)]; static void Main(string[] args) { for (int i = 0; i < StartState.Count; i++) { StartState[i]--; EndState[i]--; } int startStateIndex = StateToNum(StartState); int endStateIndex = StateToNum(EndState); for (int i = 0; i < MovesToState.Length; i++) MovesToState[i] = -1; MovesToState[startStateIndex] = 0; StateQueue.AddFirst(startStateIndex); while (StateQueue.Count > 0 && MovesToState[endStateIndex] == -1) { var legalMoves = LegalMoves(StateQueue.Last.Value); foreach (var newStateIndex in legalMoves) { int currMoves = MovesToState[StateQueue.Last.Value]; if (MovesToState[newStateIndex] == -1) { MovesToState[newStateIndex] = currMoves + 1; StateQueue.AddFirst(newStateIndex); } } StateQueue.RemoveLast(); } var currStateIndex = endStateIndex; var moves = new List<Tuple<int, int>>(); while (currStateIndex != startStateIndex) { var legalMoves = LegalMoves(currStateIndex); int currMoves = MovesToState[currStateIndex]; foreach (var prevStateIndex in legalMoves) { if (MovesToState[prevStateIndex] == MovesToState[currStateIndex] - 1) { var currState = NumToState(currStateIndex); var prevState = NumToState(prevStateIndex); for (int i = 0; i < N; i++) { if (currState[i] != prevState[i]) { moves.Add(new Tuple<int, int>(prevState[i] + 1, currState[i] + 1)); currStateIndex = prevStateIndex; break; } } } } } Console.WriteLine(MovesToState[endStateIndex]); moves.Reverse(); foreach (var move in moves) { Console.WriteLine("{0} {1}", move.Item1, move.Item2); } Console.Read(); } static List<int> LegalMoves(int stateIndex) { var legalMoves = new List<int>(); var state = NumToState(stateIndex); int[] minOnPeg = new int[K]; for (int i = 0; i < minOnPeg.Length; i++) minOnPeg[i] = N; for (int i = 0; i < N; i++) for (int j = 0; j < K; j++) if (state[i] == j && i < minOnPeg[j]) minOnPeg[j] = i; bool[] isTop = new bool[N]; for (int i = 0; i < isTop.Length; i++) isTop[i] = false; for (int i = 0; i < K; i++) if (minOnPeg[i] < N) isTop[minOnPeg[i]] = true; for (int i = 0; i < N; i++) { if (!isTop[i]) continue; for (int j = 0; j < K; j++) { if (minOnPeg[j] <= i) continue; var tmp = state[i]; state[i] = j; var newStateIndex = StateToNum(state); legalMoves.Add(newStateIndex); state[i] = tmp; } } return legalMoves; } static int StateToNum(List<int> state) { int r = 0; int f = 1; foreach (var peg in state) { r += f * peg; f *= K; } return r; } static List<int> NumToState(int num) { var r = new List<int>(); for (int i = 0; i < N; i++) { r.Add(num % K); num = num / K; } return r; } } 
+8
source

Found this solution in java. Basically, it displays all possible movements in the tree and performs BFS.

+2
source

excellent resource for solving the tower of Hanoi problem using recursion http://sleepingthreads.blogspot.in/2013/05/the-power-of-recursion_3.html

+2
source

The blog has a good test for your algorithm at the link above (note that MAX_MOVES should be increased to 11):

 6 4 3 3 2 1 4 1 1 4 2 2 3 4 

The Ruby version from Leandro Facchinetti from this comment resolves it in ~ 10 seconds. Java version by e-digga ~ 0.5 seconds. My python version runs at ~ 30 ms. I am not sure why my implementation is so fast. Here he is:

 import sys MAX_MOVES = 11 def valid_moves(state, K): pegs, tops = [-1] * K, [] for r, peg in enumerate(state): if pegs[peg] < 0: pegs[peg] = r for top_r, top_peg in tops: yield (top_r, top_peg, peg) tops.append((r, peg)) for dst_peg, peg_r in enumerate(pegs): if peg_r < 0: for top_r, top_peg in tops: yield (top_r, top_peg, dst_peg) def move_apply(state, move): r, src, dst = move return state[:r] + (dst,) + state[r + 1:] def solve_bfs(initial_state, final_state, K): known_states = set() next_states = [(initial_state, [])] depth = 0 while next_states and depth < MAX_MOVES: states, next_states = next_states, [] for state, moves in states: for move in valid_moves(state, K): new_state = move_apply(state, move) if new_state in known_states: continue new_moves = moves + [move] if new_state == final_state: return new_moves next_states.append((new_state, new_moves)) known_states.add(new_state) depth += 1 lines = sys.stdin.readlines() N, K = [int(i) for i in lines[0].strip().split()] initial_state = tuple(int(i) - 1 for i in lines[1].strip().split()) final_state = tuple(int(i) - 1 for i in lines[2].strip().split()) solution = solve_bfs(initial_state, final_state, K) if solution: print len(solution) for disk, src, dst in solution: print src + 1, dst + 1 
0
source

First post here. I used stackoverflow as a lurker, but never did, so I decided that I would do it this time, since I have the code to share.

I also tried this problem and it took me a while to figure it out! I thought that I would talk about my hard work here. I could not solve this problem in 45 minutes. I am a computer engineering student (not computer science), so I am rusting on my data structures. I have never had to encode BFS before!

In any case, my program works using the same BFS method as in the previous answers, but the data structures can be processed somewhat differently. It basically generates the actual width of the movements first (implemented with the FIFO queue), and then puts the new peg / disk configurations (states) into a dictionary with the format {state: previous_state}. After determining the final state or reaching the maximum depth of recursion, programs stop searching for new states. He then extracts the final state from the dict and backtracks through the dict to find the steps taken sequentially. The main data structure that I use in the program is a dict with various states in it. In fact, I do not record (from, to) the binding numbers needed for the output. This output is calculated when I return through the dict tree, finding one changed element between two consecutive states. States are recorded as tuples in the same format as the input.

Hope this helps someone, and I welcome any comments or suggestions on how I can improve my code or coding style.

 import sys MAX_MOVES = 10 def main(): lines = sys.stdin.readlines() [discs, npegs] = map(int, lines[0].split()) #read in the initial and final states istate = tuple(int(n) - 1 for n in lines[1].split()) fstate = tuple(int(n) - 1 for n in lines[2].split()) #call recursive function to find possible moves and #generate new states to add to tree tree = findStates(istate, fstate, npegs) solution = findSolution(istate, fstate, tree) if solution: print solution[0] for a, b in solution[1:]: print "{} {}".format(a,b) else: print "No solution found for {} max moves".format(MAX_MOVES) def findTopDisks(state, npegs): """ list the pegs with disks and the top disk radius in a dict with key being peg number and value being disk radius This function is used to find valid disks and their peg positions to make a move from """ topdict = dict() for peg in range(npegs): if peg in state: topdict[peg] = state.index(peg) return topdict def findValidMoves(state, npegs): """ Finds the valid moves given the current state and number of pegs. Yields tuples consisting of source and destination pegs """ #find the top disk of every peg number top_disks = findTopDisks(state, npegs) for from_peg, disk_r in top_disks.items(): for dest_peg in range(npegs): if not top_disks.has_key(dest_peg): #dest peg is empty yield (from_peg, dest_peg) elif top_disks[dest_peg] > disk_r: yield (from_peg, dest_peg) def findStates(istate, fstate, npegs): """ Generates new states first by calling findValidMoves on current state to get valid moves. Then generates the new states and put them in the tree implemented using a dict. Key of the dict is the current state, and value is the state that leads to that state. """ queue = [(istate, 0)] tree = {istate: None} while queue and (queue[0][1] < MAX_MOVES): cstate = queue[0][0] cmove = queue[0][1] queue.pop(0) #enumerate and find all valid moves and add to queue for from_peg, dest_peg in findValidMoves(cstate, npegs): if from_peg in cstate: nstate = list(cstate) nstate[cstate.index(from_peg)] = dest_peg nstate = tuple(nstate) if nstate not in tree: #new state never been found! tree[nstate] = cstate if nstate == fstate: return tree queue.append((nstate, cmove+1)) return tree def findSolution(istate, fstate, tree): """ back track through dict and find the moves taken to get from istate and final state """ solution = [] cstate = fstate if fstate in tree: while (cstate != istate): #compare current state and previous #find the difference, record in tuple and add to list of solution moves pstate = tree[cstate] for p, c in zip(pstate, cstate): if p != c: solution.insert(0, (p+1, c+1)) #add one to adjust for 0 offset break cstate = pstate solution.insert(0, len(solution)) return solution if __name__ == "__main__": main() 
0
source

Here is the Java code that prepares the graph with neighboring configurations and solves it. I tried to use the Object-Oriented way, but I still feel that it might be better to hack a way to solve this problem faster. Hope this helps.

 import FBSample.Node.Move; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Scanner; import java.util.Set; import java.util.Stack; import java.util.TreeMap; /** * * @author Sada Kurapati <sadakura pati@gmail.com > */ public class FBSample { public static void main(String args[]) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int k = sc.nextInt(); //pegs initial config Node source = readPegsConfiguration(k, n, sc); //read target configuration Node target = readPegsConfiguration(k, n, sc); //To keep track what config we visited and avoid cycles Set<Node> visited = new HashSet<Node>(); try { minMovesToTarget(source, target, visited); } catch (Exception ex) { System.out.println("Exception = " + ex); } } private static void minMovesToTarget(Node source, Node target, Set<Node> visited) throws CloneNotSupportedException { //Perform BFS //add soource node to Queue Queue<Node> q = new LinkedList<Node>(); q.add(source); Node current = source; while (!q.isEmpty()) { current = q.poll(); if (current.equals(target)) { //found the target configuration break; } List<Node> neighbors = current.neighbors(); if (neighbors.size() > 0) { for (Node n : neighbors) { if (!visited.contains(n)) {//if not visited, put it in queue q.offer(n); visited.add(n); } } } } //Printing path and moves if target config found if (current.equals(target)) { printOutput(current); } } private static Node readPegsConfiguration(int k, int n, Scanner sc) { Stack<Integer>[] initialState = new Stack[k]; for (int i = 0; i < k; i++) { initialState[i] = new Stack<Integer>(); } //reading and reversing the line as we need to put the elements in decresing size //disc is key and peg is value. TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(Collections.reverseOrder()); for (int i = 0; i < n; i++) { map.put(i, sc.nextInt()); } //prepare pegs for (Map.Entry<Integer, Integer> entry : map.entrySet()) { initialState[entry.getValue() - 1].push(entry.getKey()); } return new Node(initialState); } static void printOutput(Node target) { Stack<Move> stack = new Stack<>(); //using stack as we need to print the trail from Source - target config while (target.parent != null) { stack.add(target.move); target = target.parent; } System.out.println(stack.size()); while (!stack.isEmpty()) { System.out.println(stack.pop()); } } static class Node implements Cloneable { //pegs Stack<Integer>[] state = null; Node parent = null; //for backtracking trail Move move = null; // The move we made to go to next neighbor config public Node(Stack<Integer>[] st) { state = st; } @Override protected Node clone() throws CloneNotSupportedException { Stack<Integer>[] cloneStacks = new Stack[state.length]; for (int i = 0; i < state.length; i++) { cloneStacks[i] = (Stack) state[i].clone(); } Node clone = new Node(cloneStacks); return clone; } //returns the neghboring configurations. //What all configurations we can get based on current config. public List<Node> neighbors() throws CloneNotSupportedException { List<Node> neighbors = new ArrayList<>(); int k = state.length; for (int i = 0; i < k; i++) { for (int j = 0; j < k; j++) { if (i != j && !state[i].isEmpty()) { Node child = this.clone(); //make a move if (canWeMove(child.state[i], child.state[j])) { child.state[j].push(child.state[i].pop()); //this is required to backtrack the trail once we find the target config child.parent = this; //the move we made to get to this neighbor child.move = new Move(i, j); neighbors.add(child); } } } } return neighbors; } public boolean canWeMove(Stack<Integer> fromTower, Stack<Integer> toTower) { boolean answer = false; if (toTower.isEmpty()) {// if destination peg is empty, then we can move any disc return true; } int toDisc = toTower.peek(); int fromDisc = fromTower.peek(); if (fromDisc < toDisc) { //we can only place small disc on top answer = true; } return answer; } @Override public int hashCode() { int hash = 7; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Node other = (Node) obj; if (!Arrays.deepEquals(this.state, other.state)) { return false; } return true; } class Move { int pegFrom, pegTo; public Move(int f, int t) { pegFrom = f + 1; pegTo = t + 1; } @Override public String toString() { return pegFrom + " " + pegTo; } } } } 
0
source

Source: https://habr.com/ru/post/945222/


All Articles