Подсчитайте количество островов в 2d-матрице с вариацией

«По заданной логической двумерной матрице найдите количество островов. Группа соединенных единиц образует остров».

В этом варианте вопроса о подсчете количества островов мы должны подсчитать количество островов, ПОЛНОСТЬЮ окруженных водой. То есть не должны учитываться ни единицы на краю, ни остров на краю. Нам нужны только единицы, окруженные только нулями со всех сторон.

Я попытался использовать популярную технику dfs с некоторыми модификациями. Я не пересекаю все края матрицы. Это, очевидно, удовлетворяет только нескольким случаям. В следующем примере это не удается, так как вместо 1 возвращается 2:

пример матрицы

Я также пытался уменьшать счетчик каждый раз, когда возвращаюсь назад, но это, очевидно, бесполезное усилие, потому что счет в конечном итоге становится меньше нуля. Наконец, я попытался уменьшить количество, установив нижнюю границу на 0. Это просто возвращает 0 все время. Я определенно что-то упускаю. Любая помощь?

Вот код ниже:

class Islands { 
    // No of rows and columns 
    static final int ROW = 5, COL = 5; 
    int gcount;

    // A function to check if a given cell (row, col) can 
    // be included in DFS 
    boolean isSafe(int M[][], int row, int col, 
                   boolean visited[][]) 
    { 
        // row number is in range, column number is in range 
        // and value is 1 and not yet visited 
        return (row >= 1) && (row < ROW-1) && (col >= 1) && // used to be row >= 0 && row < ROW && col >=0
                (col < COL-1) && (M[row][col] == 1 && !visited[row][col]); //col < COL
    } 

    // A utility function to do DFS for a 2D boolean matrix. 
    // It only considers the 8 neighbors as adjacent vertices 
    void DFS(int M[][], int row, int col, boolean visited[][]) 
    { 
        // These arrays are used to get row and column numbers 
        // of 8 neighbors of a given cell 
        int rowNbr[] = new int[] { -1, -1, -1, 0, 0, 1, 1, 1 }; 
        int colNbr[] = new int[] { -1, 0, 1, -1, 1, -1, 0, 1 }; 

        // Mark this cell as visited 
        visited[row][col] = true; 

        // Recur for all connected neighbors 
        for (int k = 0; k < 8; ++k) 
            if (isSafe(M, row + rowNbr[k], col + colNbr[k], visited)) {
                System.out.println("here");
                ++gcount;
                DFS(M, row + rowNbr[k], col + colNbr[k], visited);
            }
            /*else {
                gcount--;
                if(gcount < 1) {
                    gcount = 0;
                }
            }*/
    } 

    // The main function that returns count of islands in a given 
    // boolean 2D matrix 
    int countIslands(int M[][]) 
    { 
        // Make a bool array to mark visited cells. 
        // Initially all cells are unvisited 
        boolean visited[][] = new boolean[ROW][COL]; 

        // Initialize count as 0 and traverse through the all cells 
        // of given matrix 
        int count = 0; 
        for (int i = 1; i < ROW-1; ++i) //I changed this from ROW
            for (int j = 1; j < COL-1; ++j) //I changed this from COL
                if (M[i][j] == 1 && !visited[i][j]) // If a cell with 
                { // value 1 is not 
                    // visited yet, then new island found, Visit all 
                    // cells in this island and increment island count 
                    DFS(M, i, j, visited); 
                    //++gcount; 
                } 

        return gcount; 
    } 

person Duck Dodgers    schedule 19.05.2020    source источник
comment
Я определенно что-то упускаю — да, ваш код. Опубликуйте, и тогда мы сможем помочь   -  person Scary Wombat    schedule 19.05.2020
comment
Вы проверяете все 8 окружающих элементов? Где твой код.   -  person foobar    schedule 19.05.2020
comment
Извините, я только что опубликовал код @foobar   -  person Duck Dodgers    schedule 19.05.2020


Ответы (1)


В countIslands сначала посетите все граничные ячейки (верхние и нижние строки, левый и правый столбцы). Это пометит все острова, достижимые из пограничной ячейки, как посещенные.

for (int j = 0; j < COL; ++j)
{
    if (M[0][j] == 1 && !visited[0][j])
        DFS(M, 0, j, visited);
    if (M[ROW - 1][j] == 1 && !visited[ROW - 1][j])
        DFS(M, ROW - 1, j, visited);
}

for (int i = 1; i < ROW - 1; ++i) // I changed this from ROW
{
    if (M[i][0] == 1 && !visited[i][0])
        DFS(M, i, 0, visited);
    if (M[i][COL - 1] == 1 && !visited[i][COL - 1])
        DFS(M, i, COL - 1, visited);
}

Затем вы посещаете внутренние камеры, как и сейчас.

int count = 0;
for (int i = 1; i < ROW - 1; ++i)
{
    for (int j = 1; j < COL - 1; ++j)
    {
        if (M[i][j] == 1 && !visited[i][j])
        {
            DFS(M, i, j, visited);
            count++;
        }
    }
}

Обратите внимание, что вам нужно увеличить счетчик здесь, а не в DFS, так как DFS вызывается и для краевых островов.

И последнее замечание: эти массивы должны быть объявлены на уровне класса, то есть статически, а не в методе DFS. Нет необходимости создавать их при каждом рекурсивном вызове.

    int rowNbr[] = new int[] { -1, -1, -1, 0, 0, 1, 1, 1 }; 
    int colNbr[] = new int[] { -1, 0, 1, -1, 1, -1, 0, 1 }; 
person RaffleBuffle    schedule 19.05.2020
comment
Ах понятно. Сначала мы запускаем DFS с краев. Это помогает устранить все островки, выступающие за края. Аккуратный! - person Duck Dodgers; 19.05.2020