0

Problem:

In a given grid, each cell can have one of three values:

the value 0 representing an empty cell;

the value 1 representing a fresh orange;

the value 2 representing a rotten orange.

Every minute, any fresh orange that is adjacent (4-directionally) to a rotten orange becomes rotten. Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1 instead. source

Approach:

I am trying to solve this problem in BFS manner. I first push every occurrence of a rotten orange into a queue. Then I go through the grid counting the occurrences of fresh oranges near the rotten ones and set that current position to be a rotten orange.

The problem is that if there is more than one rotten orange, they should be processed "at the same time". But my code currently processes one orange, increments time and then processes the next orange.

Thus for this test case std::vector<std::vector<int>> grid = { {1, 2, 1, 1, 2, 1, 1} }; the result should be 2 and the code prints 3.

Code:

#include <vector>
#include <iostream>
#include <queue>

using namespace std;

   class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int time = 0;
        if (grid.empty())
            return time;


        std::queue<std::pair<int, int>> q;
        for (int i = 0; i < grid.size(); ++i)
        {
            for (int j = 0; j < grid[i].size(); ++j)
            {
                if (grid[i][j] == 2)
                    q.push({ i,j });
            }
        }

        std::vector<std::vector<int>> dirs = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
        while (!q.empty())
        {
            int n = q.size();

            auto p = q.front();
            q.pop();
            n--;

            for (auto dir : dirs)
            {
                int row = p.first;
                int col = p.second;

                if (isValid(grid, row + dir[0], col + dir[1]))
                {
                    row += dir[0];
                    col += dir[1];
                    if (n == 0)
                    {
                        time++;
                        n = q.size();
                    }
                    q.push({ row, col });
                    grid[row][col] = 2;
                }
            }
        }

        for (int i = 0; i < grid.size(); i++)
        {
            for (int j = 0; j < grid[i].size(); ++j)
            {
                if (grid[i][j] == 1)
                {
                    time = -1;
                    break;
                }
            }
        }

        return time;
    }

    bool isValid(vector<vector<int>>& grid, int i, int j)
    {
        if (i < 0 || i >= grid.size() || j < 0 || j >= grid[i].size() || grid[i][j] != 1)
            return false;
        return true;
    }
};


int main()
{

    Solution obj;
    std::vector<std::vector<int>> grid = { {1, 2, 1, 1, 2, 1, 1} };
    auto result = obj.orangesRotting(grid);
    int correct_solution = 2;

    if (result == correct_solution)
        std::cout << "Correct!";

    else
        std::cout << "Not correct!";

    std::cin.get();
}

Question:

I am not sure how to deal with the fact that if there is more than one rotten orange, they should be processed "at the same time". Any ideas?

Update:

Attempting the first comments approach:

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int time = 0;
        if (grid.empty())
            return time;


        std::queue<std::pair<int, int>> q;
        for (int i = 0; i < grid.size(); ++i)
        {
            for (int j = 0; j < grid[i].size(); ++j)
            {
                if (grid[i][j] == 2)
                    q.push({ i,j });
            }
        }

        std::vector<std::vector<int>> dirs = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
        while (!q.empty())
        {
            int n = q.size();

            for (int i = 0; i < n; ++i)
            {
                auto p = q.front();
                q.pop();
                n--;

                for (auto dir : dirs)
                {
                    int row = p.first;
                    int col = p.second;

                    if (isValid(grid, row + dir[0], col + dir[1]))
                    {
                        row += dir[0];
                        col += dir[1];
                        if (n == 0)
                        {
                            time++;
                        }
                        q.push({ row, col });
                        grid[row][col] = 2;
                    }
                }
            }
        }

        for (int i = 0; i < grid.size(); i++)
        {
            for (int j = 0; j < grid[i].size(); ++j)
            {
                if (grid[i][j] == 1)
                {
                    time = -1;
                    break;
                }
            }
        }

        return time;
    }

    bool isValid(vector<vector<int>>& grid, int i, int j)
    {
        if (i < 0 || i >= grid.size() || j < 0 || j >= grid[i].size() || grid[i][j] != 1)
            return false;
        return true;
    }
};
Snorrlaxxx
  • 168
  • 1
  • 3
  • 18
  • 2
    One possible approach. Have a variable that stores the initial size of the queue. Whenever you `pop`, decrement it. When it reaches zero, that means you processed one "generation" of rotten oranges - increment the time and reset the variable to then-current size of the queue (the number of oranges in the next generation). – Igor Tandetnik Dec 14 '19 at 04:40
  • 2
    Another approach. Have two queues, one for the current generation, and one for the next. `pop` from the first, `push` to the second. When the first becomes empty, increment the time counter, swap two queues, repeat. – Igor Tandetnik Dec 14 '19 at 04:42
  • @IgorTandetnik I like the first approach, do you know when I need to check the size of the queue is zero and then increment time? I got rid of the flag check and I thought I would check and increment time inside the if isValid check – Snorrlaxxx Dec 14 '19 at 05:00
  • You don't check the size of the queue - you check a separate variable that is initialized to the size of the queue, and decremented with each `pop` (but, unlike the size of the queue, *not* incremented with each `push`). In effect, you have two queues concatenated into one, and that variable track where the first ends and the second begins. – Igor Tandetnik Dec 14 '19 at 05:15
  • @IgorTandetnik I am not sure I get it could you provide a pseudocode solution? – Snorrlaxxx Dec 14 '19 at 05:17
  • @IgorTandetnik I made an update with your idea but not passing this test case:[[1],[2],[2]] – Snorrlaxxx Dec 14 '19 at 05:20
  • Your inner `for` loop only makes half the number of iterations intended, as you both increment `i` and decrement `n`. You don't need that inner loop at all. – Igor Tandetnik Dec 14 '19 at 05:22
  • @IgorTandetnik How can I check the direction then if I do not have the inner for loop? – Snorrlaxxx Dec 14 '19 at 05:24
  • I meant second inner - `for (int i = 0; i < n; ++i)` loop you added that wasn't there before. – Igor Tandetnik Dec 14 '19 at 05:24
  • I see, well removing it didn't change much, perhaps I am not getting the idea still – Snorrlaxxx Dec 14 '19 at 05:30
  • Well, you also need the part where you check whether `n` reached zero, and if it did increment time counter and reset `n` to the then-current size of the queue. – Igor Tandetnik Dec 14 '19 at 05:31
  • Do I do the n == 0 check inside the if(isValid()) part? Thats where I put it but I still dont get the correct result, ill make an update to the code to show you – Snorrlaxxx Dec 14 '19 at 05:35
  • You check it for zero right after you decrement it. And, you don't do `int n = q.size();` every time through the loop - that defeats the whole point. You do it before the loop, and then each time it reaches zero. – Igor Tandetnik Dec 14 '19 at 05:37
  • Okay it passed the test case above but not this one [[0,2]] – Snorrlaxxx Dec 14 '19 at 05:42

0 Answers0