Search code examples
c++recursionbacktracking

Leetcode question 489. Robot Room Cleaner - why my solution doesn't work


I am trying to solve the Leetcode question 489. Robot Room Cleaner using backtracking. Specifically, I try to move the robot in each of 4 directions and backtrack if all four directions are blocked or cleaned.

The code below doesn't work and I am trying to debug it with this simple example:

grid = [[1,1,1],
        [1,0,1]]

where 1 means cell is accessible by a robot and 0 means cell is blocked. The robot starts at this initial position:

initial_row = 0
initial_col = 1

After I run my code, the the robot only cleaned the right part of the grid (c - cleaned, d - left dirty):

[[d,c,c],
 [d,0,c]]

It seems to backtrack correctly to the cell [0,1], and it tries to move to the upper-left corner (cell [0,0]), but the robot.move() returns false and the function returns before cleaning the upper-left corner. I would appreciate any suggestions of what might be going wrong.

Code:

struct hsh {
  size_t operator() (const pair<int,int>& p) const {
      hash<int> intHash;
      return intHash(p.first) ^ intHash(p.second);
  }  
};

class Solution {
public:
    unordered_set<pair<int, int>, hsh> cleaned;
    vector<int> directions {0, 1, 2, 3}; // up, down, right, left
    
    void cleanRoom(Robot& robot) {
       
        int row_s = 0; // relative start row
        int col_s = 0; // relative start col
            
        clean(row_s, col_s, robot);
    }

    void clean(int row, int col, Robot& robot) {
    
        cleaned.insert({row, col});
        robot.clean();
    
        for (int dir : directions) {
        
            if (!is_cleaned(row, col, dir)) {
                turn(robot, dir);
            
                if (robot.move()) {
                    turn_back(robot, dir);
                
                    int row_new = get_new_row(row, dir);
                    int col_new = get_new_col(col, dir);
                
                    clean(row_new, col_new, robot);
                } else {
                    turn_back(robot, dir);
                }
            }
        }
    }

    bool is_cleaned(int row, int col, int dir) {
        int row_new = get_new_row(row, dir);
        int col_new = get_new_col(col, dir);
            
        if(cleaned.find({row_new, col_new}) != cleaned.end()) {
            return true;
        }
        return false;
    
    }

    void turn(Robot& robot, int dir) {
        if (dir == 0) { // up
        } else if (dir == 1) { // down
            robot.turnLeft();
            robot.turnLeft();
        } else if (dir == 2) { // right
            robot.turnRight();
        } else { // left
            robot.turnLeft();
        }
    }

    void turn_back(Robot& robot, int dir) {
        if (dir == 0) { // back to up from up
        } else if (dir == 1) { // back to up from down
            robot.turnLeft();
            robot.turnLeft();
        } else if (dir == 2) { // back to up from right
            robot.turnLeft();
        } else { // back to up from left
            robot.turnRight();
        }
    }

    int get_new_row(int row, int dir) {
        if (dir == 0) { // up
            row--; 
            return row;
        } else if (dir == 1) { // down
            row++; 
            return row;
        }
    
        return row;
    }

    int get_new_col(int col, int dir) {
        if (dir == 2) { // right
            col++; 
            return col;
        } else if (dir == 3) { // left
            col--; 
            return col;
        }
    
        return col;
    }
};

Solution

  • The problem was that the Robot class interface doesn't put the robot back automatically when backtracking (Robot object is passed by reference and keeps its position). Adding a specific function to move the robot back after backtracking fixed the problem:

    void go_back(Robot& robot, int dir) {
        // Keep mind that the robot always ends up facing up after every move
        if (dir == 0) { // moved up - returning down
            robot.turnLeft();
            robot.turnLeft();
            robot.move();
            robot.turnLeft();
            robot.turnLeft();
        } else if (dir == 1) { // moved down - returning up
            robot.move();
        } else if (dir == 2) { // moved right - returning left
            robot.turnLeft();
            robot.move();
            robot.turnRight();
        } else { // moved left - returning right
            robot.turnRight();
            robot.move();
            robot.turnLeft();
        }
    }
    

    Then call this function within the recursive clean() function:

    void clean(int row, int col, Robot& robot) {
        
        cleaned.insert({row, col});
        robot.clean();
                
        for (int dir : directions) {
                        
            if (!is_cleaned(row, col, dir)) {
                turn(robot, dir);
                
                if (robot.move()) {
                    turn_back(robot, dir);
                    
                    int row_new = get_new_row(row, dir);
                    int col_new = get_new_col(col, dir);
                    
                    clean(row_new, col_new, robot);
                    
                    go_back(robot, dir); // Move the robot back while backtracking!
                    
                } else {
                    turn_back(robot, dir);
                }
            }
        }
        
    }