找出给定矩阵中最大的盆地大小

时间:2014-06-20 22:41:12

标签: c++ arrays algorithm graph

问题:

这是一个面试问题。

一群农民有一些海拔数据,我们将帮助他们了解降雨如何流过他们的农田。

我们将土地视为一个二维高度阵列,并根据水流下坡的想法使用以下模型:

如果一个小区的八个相邻小区都有更高的高度,我们称这个小区为盆地;水收集在盆地中。

否则,水将流向海拔最低的相邻牢房。

直接或间接排入同一水槽的细胞据说是同一盆地的一部分。

以下是一些例子:

输入:

1 1 2
1 1 7
3 6 9

4号

9 9 9 8 7 7
8 8 7 7 7 8
8 8 8 7 7 7
8 8 8 9 9 9
8 8 8 7 7 7
4 4 5 5 5 5
5 5 5 6 6 7
5 5 5 8 8 6

8号

9 9 9 8 8 8
8 8 8 7 7 7
7 7 7 7 7 7
8 8 8 8 9 9
5 5 5 5 6 3
5 5 5 3 3 3

9号

突出显示的值构成了最大尺寸的盆地。

所以问题是

将地图划分为盆地。特别是,给定一个高程地图,您的代码应该将地图划分为盆地并输出最大盆地的大小。 我们需要突出最大尺寸的盆地。

如果问题出现了这个假设

“如果某个单元格不是接收器,您可能会认为它具有唯一的最低邻居,并且该邻居将低于该单元”

然后我可以想到这个解决方案

    Each array element is a node in a graph. Construct the graph adding edges between the nodes:
  1 If node A is the smallest among all of its own neighbors, don't add an edge (it's a sink)
  2 There is an edge between two neighbors A and B iff A is the smallest of all neighbors of B.
  3 Finally traverse the graph using BFS or DFS and count the elements in the connected components.

现在我已经实现了算法的第三部分

#include<iostream>
#include<vector>
#include<string.h>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int cv[1000]; // array stores number of nodes in each connected components
int main()
{
queue<int>q;
bool visited[100000];

int t,i,j,x,y,cvindex=0;
int n,e;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&e);
vector< vector<int> >G(n);
memset(visited,0,sizeof(visited));

for(i=0;i<e;i++)
{
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}

int ans=0;
for(i=0;i<n;i++)
{
if(!visited[i]) 
{
q.push(i);
visited[i]=1;
cv[cvindex]++;

while(!q.empty())
{
int p=q.front();
q.pop();
for(j=0;j<G[p].size();j++)
{
if(!visited[G[p][j]])
{
visited[G[p][j]]=1;
q.push(G[p][j]);
cv[cvindex]++;
}
}
}
ans++;
cvindex++;
}
}
printf("%d\n",ans);
sort(cv,cv+cvindex);
for(int zz=0;zz<cvindex;zz++)
printf("%d ",cv[zz]);
}
}   

时间复杂度O(n * m)

但如何在没有假设的情况下解决上述问题? 我想要几乎类似的方法稍加修改。

欢迎其他算法。

此外,在时间复杂度方面是否存在更好的算法?

4 个答案:

答案 0 :(得分:1)

将高程图构造为图形,其中2d数组的每个元素都是一个节点。此外,如果节点u的高程=节点v的高程,则存在从节点u到节点v的有向边。构造该图的SCC并选择最大的组件。这将是你正在寻找的盆地。

答案 1 :(得分:1)

这是我的工作代码。我还评论了我的每一步,以便您理解。如果你仍然找到了一些帮助,你可以问。

<强>算法

  1. 首先根据他们的高度存储索引。
  2. 然后从最小高度迭代到最大高度。
  3. 如果尚未访问当前索引,则将其设为盆地表面(水可以收集的地方),并使所有高度大于此的邻居为非盆地表面。
  4. 重复步骤3,直到访问所有索引。
  5. 然后在判断每个索引的状态之后。我们需要找到最大的盆地表面。我们可以通过使用DFS找到。
  6. 时间复杂度:O(ROWS * COLUMNS)

    #include<iostream>
    #include<vector>
    #include<string.h>
    #include<climits>
    
    #define BASIN 1
    #define NOT_BASIN 2
    #define NOT_DEFINED_YET 3
    
    #define ROW 1000
    #define COLUMN 1000
    #define MAXIMUM_HEIGHT_POSSIBLE 1000
    
    using namespace std;
    
    int heights[ROW][COLUMN];  // It stores the height
    int maximumBasin[ROW][COLUMN]; // It stores the state of each index, Total 3 states possible, ( BASIN, NOT_BASIN, NOT_DEFINED_YET )
    bool alreadyVisited[ROW][COLUMN]; // True, if currect index visited, otherwise false.
    vector< pair<int, int> > heightsCoordinates[MAXIMUM_HEIGHT_POSSIBLE]; // It stores all the indexs of given height.
    int N, M, maxHeightPossible;
    
    int dx[] = {0, 1, 1, 1, 0, -1, -1, -1};
    int dy[] = {1, 1, 0, -1, -1, -1, 0, 1};
    
    bool isValidLocation(int x, int y) {
        if(x < 0 || x > M || y < 0 || y > N || alreadyVisited[x][y] == true) return false;
        return true;
    }
    
    void DFS_FOR_MARKING_WITH_GIVEN_VALUE(int value, int x, int y) {
        maximumBasin[x][y] = value;
        alreadyVisited[x][y] = true;
        for(int i = 0; i < 8; i++) if( isValidLocation(x + dx[i], y + dy[i]) && heights[x + dx[i]][y + dy[i]] >= heights[x][y] ) DFS_FOR_MARKING_WITH_GIVEN_VALUE(value, x + dx[i], y + dy[i]);
    }
    
    void DFS_FOR_COUNTING_BASINS_TOGETHER(int &cnt, int x, int y) {
        cnt++;
        alreadyVisited[x][y] = true;
        for(int i = 0; i < 8; i++) if( isValidLocation(x+dx[i], y+dy[i]) && maximumBasin[x + dx[i]][y + dy[i]] ==  BASIN ) DFS_FOR_COUNTING_BASINS_TOGETHER(cnt, x + dx[i], y + dy[i]);
    }
    
    void printBasin() {
        for(int i = 0; i < M; i++) {
            for(int j = 0; j < N; j++) cout << maximumBasin[i][j] << "  ";
            cout << endl;
        }
    }
    
    main() {
    
        cin >> M >> N >> maxHeightPossible;
        int x, y;
        int maximumCounts = INT_MIN;
        int cntTemp = 0;
    
        /**
         Take input and set NOT_DEFINED_YET for maximumBasin.
        **/
        for(int i = 0; i < M; i++) {
            for(int j = 0; j < N; j++) {
                 cin >> heights[i][j];
                 maximumBasin[i][j] = NOT_DEFINED_YET;
                 heightsCoordinates[ heights[i][j] ].push_back(pair<int, int>(i, j));
            }
        }
    
        /**
        Iterate from smallest to largest height.
        If current index is  "NOT_DEFINED_YET" (means it is the candidate index where water can collect).  Water will come here from all neighbourhood whose height is greater than this.
        For that I call DFS_FOR_MARKING_WITH_GIVEN_VALUE function.
        **/
        for( int i = 0; i <= maxHeightPossible; i++ ){
            if(heightsCoordinates[i].size() == 0) continue;
            for(int j = 0; j < heightsCoordinates[i].size(); j++) {
                x = heightsCoordinates[i][j].first;
                y = heightsCoordinates[i][j].second;
                if( maximumBasin[x][y] == NOT_DEFINED_YET ) {
                    maximumBasin[x][y] = BASIN;
                    alreadyVisited[x][y] = true;
                    for(int k = 0; k < 8; k++) {
                        if( isValidLocation( x + dx[k], y + dy[k] ) ) {
                            if ( heights[x + dx[k]][ y + dy[k]] > heights[x][y] ) {
                                DFS_FOR_MARKING_WITH_GIVEN_VALUE(NOT_BASIN, x + dx[k], y + dy[k]);
                            }
                        }
                    }
                }
                else {
                    // If  it is set by BASIN or NOT_BASIN, Shows already processed before.
                }
            }
        }
    
        //printBasin();
    
        memset(alreadyVisited, 0, sizeof(alreadyVisited));
    
        /**
            It simply counts basins which are together.
        **/
        for(int i = 0; i < M; i++) {
            for(int j = 0; j < N; j++) {
                if( alreadyVisited[i][j] == false && maximumBasin[i][j] == BASIN) {
                    DFS_FOR_COUNTING_BASINS_TOGETHER(cntTemp, i, j);
                    //cout << cntTemp << endl;
                    if(cntTemp > maximumCounts ) maximumCounts = cntTemp;
                    cntTemp = 0;
                }
            }
        }
    
        /**
            This is our final Answer.
        **/
        cout << maximumCounts << endl;
        return 0;
    }
    

答案 2 :(得分:0)

我想,Watershed是您正在寻找的算法。它主要用于图像分割,但它基于灰度图像中的盆地,因此我认为适用于您的问题。

答案 3 :(得分:0)

<强>算法

1-根据优先级队列中的高度插入所有元素(最小堆) 2-逐行从队列中删除元素,直到队列为空,并将所有具有较高高度的邻居标记为非盆地。(使用深度优先遍历进一步标记高度大于等于当前邻居的邻居的邻居作为非盆地并保持删除元素从队列) 当队列为空时,所有非盆地都被标记为非盆地

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * Created by dheeraj on 12/16/14.
 */
public class FindingBasin {
    int[][] matrix;
    int rows;
    int cols;

    PriorityQueue<RowColHeight> rowColHeightPriorityQueue;

    private static class RowColHeight {
        int row;
        int col;
        int height;

        public RowColHeight(int row, int col, int height) {
            this.row = row;
            this.col = col;
            this.height = height;
        }

        @Override
        public boolean equals(Object obj) {
            RowColHeight obj1 = (RowColHeight) obj;
            if(obj1.row == this.row && obj1.col == this.col){
                return true;
            }else{
                return false;
            }
        }


    }

    public FindingBasin(int[][] matrix) {
        this.matrix = matrix;
        this.rows = matrix.length;
        this.cols = matrix[0].length;
    }

    public void findBasin() {
        rowColHeightPriorityQueue = new PriorityQueue<RowColHeight>(rows * cols, new Comparator<RowColHeight>() {
            @Override
            public int compare(RowColHeight o1, RowColHeight o2) {
                return o1.height - o2.height;
            }
        });

        // basin matrix -> true basin , false non basin
        boolean[][] basinMatrix = new boolean[rows][cols];
        boolean[][] visitedMatrix = new boolean[rows][cols];
        // sort matrix data on the basis of heights
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
                rowColHeightPriorityQueue.add(new RowColHeight(x, y, matrix[x][y]));
                basinMatrix[x][y] = true;
                visitedMatrix[x][y] = false;
            }
        }

        RowColHeight rowColHeight;

        while (!rowColHeightPriorityQueue.isEmpty()) {
            //find all non basins
            rowColHeight = rowColHeightPriorityQueue.remove();

            for (int x = Math.max(0, rowColHeight.row - 1); x < Math.min(rows, rowColHeight.row + 2); x++) {
                for (int y = Math.max(0, rowColHeight.col - 1); y < Math.min(cols, rowColHeight.col + 2); y++) {
                    if (x == rowColHeight.row && y == rowColHeight.col) {
                        continue;
                    }

                    if (visitedMatrix[x][y]) {
                        continue;
                    } else {
                        visitedMatrix[x][y] = true;
                    }

                    if (matrix[x][y] > rowColHeight.height) {
                        basinMatrix[x][y] = false;
                        //matrix[x][y] is non basin so all its neighbours >= it are also non basins
                        markALlNonBasins(rowColHeightPriorityQueue,visitedMatrix,basinMatrix,x,y);
                    }
                }
            }
        }
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
                System.out.print(basinMatrix[x][y] + " ");
            }
            System.out.println();
        }
    }

    private void markALlNonBasins(PriorityQueue<RowColHeight> rowColHeightPriorityQueue,boolean[][] visitedMatrix,boolean[][] basinMatrix,int row,int column) {

        for (int x = Math.max(0,row - 1); x < Math.min(rows,row + 2); x++) {
            for (int y = Math.max(0,column - 1); y < Math.min(cols,column + 2); y++) {
                if (x == row && y == column) {
                    continue;
                }

                if(visitedMatrix[x][y]){
                    continue;
                }

                if(matrix[x][y] >= matrix[row][column]){
                    visitedMatrix[x][y] = true;
                    basinMatrix[x][y] =false;
                    rowColHeightPriorityQueue.remove(new RowColHeight(x,y,matrix[x][y]));
                    markALlNonBasins(rowColHeightPriorityQueue,visitedMatrix,basinMatrix,x,y);
                }
            }
        }
    }

    public static void main(String[] args) {
        int[][] matrix = {
                {9, 9, 9, 8, 7, 7},
                {8, 8, 7, 7, 7, 8},
                {8, 8, 8, 7, 7, 7},
                {8, 8, 8, 9, 9, 9},
                {8, 8, 8, 7, 7, 7},
                {4, 4, 5, 5, 5, 5},
                {5, 5, 5, 6, 6, 7},
                {5, 5, 5, 8, 8, 6}
        };

        FindingBasin findingBasin = new FindingBasin(matrix);
        findingBasin.findBasin();


    }
}