【HDU - 1241】Oil Deposits (连通块问题 属于求大海中的岛屿个数 类似问题)

版权声明:欢迎学习我的博客,希望ACM的发展越来越好~ https://blog.csdn.net/qq_41289920/article/details/81056399

题干:

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid. 

InputThe input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket. 
OutputFor each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets. 
Sample Input

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5 
****@
*@@*@
*@**@
@@@*@
@@**@
0 0 

Sample Output

0
1
2
2

解题报告:

    dfs水题,但是做题效率并不好。自己想想为什么。

错误代码:(是我想多了、、、)(想法是如果周围)

#include<bits/stdc++.h>


using namespace std;


char maze[105][105];
bool bk[105][105];
int main()
{
	int cnt=1;
	int n,m;
	while(cin>>m>>n) {
		memset(bk,0,sizeof(bk) ) ;
		memset(maze,0,sizeof(maze) ); 
		if(m==0) break;
		cnt=0;
		for(int i = 1; i<=m; i++) 
		scanf("%s",maze[i]+1);
		for(int i = 1; i<=m; i++) {
			for(int j = 1; j<=n; j++) {
				if(maze[i][j]=='*') continue;
				if(bk[i][j]==1) continue;
				if( !bk[i][j+1] && !bk[i+1][j+1] && !bk[i+1][j] && !bk[i+1][j-1] && !bk[i][j-1]&& !bk[i-1][j-1]&& !bk[i-1][j]&& !bk[i-1][j+1])
				{
					cnt++;
					if(maze[i][j+1]=='@') bk[i][j+1]=1;
					if(maze[i+1][j+1]=='@') bk[i+1][j+1]=1;
					if(maze[i+1][j]=='@') bk[i+1][j]=1;
					if(maze[i+1][j-1]=='@') bk[i+1][j-1]=1;
					if(maze[i][j-1]=='@') bk[i][j-1]=1;
					if(maze[i-1][j-1]=='@') bk[i-1][j-1]=1;
					if(maze[i-1][j]=='@') bk[i-1][j]=1;
					if(maze[i-1][j+1]=='@') bk[i-1][j+1]=1;
				}
				bk[i][j]=1;//别放到那个括号里面啊!!! 
			}
		}
		printf("%d\n",cnt);
	}
	
	return 0 ;
}
	int cnt=1;
	int n,m;
	while(cin>>m>>n) {
		memset(bk,0,sizeof(bk) ) ;
		memset(maze,0,sizeof(maze) ); 
		if(m==0) break;
		cnt=0;
		for(int i = 1; i<=m; i++) 
		scanf("%s",maze[i]+1);
		for(int i = 1; i<=m; i++) {
			for(int j = 1; j<=n; j++) {
				if(maze[i][j]=='*') continue;
				if(bk[i][j]==1) continue;
				if( !bk[i][j+1] && !bk[i+1][j+1] && !bk[i+1][j] && !bk[i+1][j-1] && !bk[i][j-1]&& !bk[i-1][j-1]&& !bk[i-1][j]&& !bk[i-1][j+1])
				{
					cnt++;
					if(maze[i][j+1]=='@') bk[i][j+1]=1;
					if(maze[i+1][j+1]=='@') bk[i+1][j+1]=1;
					if(maze[i+1][j]=='@') bk[i+1][j]=1;
					if(maze[i+1][j-1]=='@') bk[i+1][j-1]=1;
					if(maze[i][j-1]=='@') bk[i][j-1]=1;
					if(maze[i-1][j-1]=='@') bk[i-1][j-1]=1;
					if(maze[i-1][j]=='@') bk[i-1][j]=1;
					if(maze[i-1][j+1]=='@') bk[i-1][j+1]=1;
				}
				bk[i][j]=1;//别放到那个括号里面啊!!! 
			}
		}
		printf("%d\n",cnt);
	}
	
	return 0 ;
}

给一个样例:

***#

*#*#

**#*

就不立了。

ac代码:

#include<bits/stdc++.h>

using namespace std;

char maze[105][105];
bool bk[105][105];
int n,m;
void dfs(int x,int y) {
	//属于先深入再判断型 。一般写深搜都是for一个方向数组,然后先判断,再进入型。 这种题用前者会减少代码量毕竟没有for循环了得一个一个的if就很麻烦 
	if(maze[x][y]!='@' || x<1 || y<1 || x>m || y>n) return;      
    maze[x][y] = 'x';//这步很关键。  
    dfs(x-1, y-1);  
    dfs(x-1, y);  
    dfs(x-1, y+1);  
    dfs(x, y-1);  
    dfs(x, y+1);  
    dfs(x+1, y-1);  
    dfs(x+1, y);  
    dfs(x+1, y+1);  
}
int main()
{
	int cnt=1;
	while(cin>>m>>n) {
		memset(bk,0,sizeof(bk) ) ;
		memset(maze,0,sizeof(maze) ); 
		if(m==0) break;
		cnt=0;
		for(int i = 1; i<=m; i++) 
		scanf("%s",maze[i]+1);
		for(int i = 1; i<=m; i++) {
			for(int j = 1; j<=n; j++) {
				if(maze[i][j]!='@') continue;//不要写 maze[i][j]=='*'!!因为你这里面还有'x'出现啊 
				dfs(i,j); 
				cnt++;
			}
		}
		printf("%d\n",cnt);
	}
	return 0 ;
}

总结:

    1.虽然是一道水题但是还是有很多问题需要注意的。可能是因为太长时间没写深搜了吧,有点小手生。

    2.本题的搜索属于先深入再判断型 。一般写深搜(尤其是地图问题)都是for一个方向数组,然后先判断,再进入型。 这种题用前者会减少代码量毕竟没有for循环了得一个一个的if就很麻烦 。

    3.不要想当然的用for循环!那样是肯定有陷阱的!由给出的那个样例你也可以发现用for遍历和dfs深搜的最主要区别就是dfs可以跳跃的选择我们想要的部分,而不是只能老老实实的挨个遍历。dfs牺牲了空间(递归层数深了还有可能栈溢出)和效率(此题并没有体现出来,因为相当于剪枝十分优雅)的同时也是有自己的优势所在的!

    4.连通块问题可否通用?一个点很实用,那就是改原地图,比如此题中走过的'@'就改为别的字符,这样避免了开一个新的vis数组了!(或者用着色问题也可以解)

5.连通块问题也可以用并查集来做!贴一个代码:

    给定一个由1和0组成的二维字符数组,1代表陆地,0代表水。问被水包围的连通陆地区域的个数。

class Solution {
public:
    vector<int> pre;
    int count=0;

    int numIslands(vector<vector<char>>& grid) {
        if(grid.size()==0 || grid[0].size()==0)
            return 0;

        int row = grid.size();
        int col = grid[0].size();
        pre.resize(row*col+1,0);

        //对pre数组进行初始化
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
            {
                int num = col*i+j;
                pre[num] = num;
            }

        //遍历图中的每个点
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
            {
                if(grid[i][j]=='1')
                {
                    int down=i+1,right=j+1;
                    if(down<row && grid[down][j]=='1')
                        join(col*i+j,col*down+j);
                    if(right<col && grid[i][right]=='1')
                        join(col*i+j,col*i+right);
                }
            }

        //再遍历一次,计算islands的个数
        int ans = 0;
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
            {
                int num = col*i+j;
                if(pre[num] == num && grid[i][j]=='1')
                    ans++;
            }
        return ans;
    }

    //并,将联通的点的pre设为同一个值
    void join(int x,int y){
        int fx=find(x);
        int fy=find(y);
        if(fx != fy)
            pre[fx] = fy;
    }

    //找到a的祖先,并且路径压缩
    int find(int a){
        if(pre[a] != a)
            pre[a] = find(pre[a]);
        return pre[a];
    }
};

贴一道类似题目 :

    蓝桥杯山东省赛习题9 全球变暖https://blog.csdn.net/qq_41289920/article/details/81056373

猜你喜欢

转载自blog.csdn.net/qq_41289920/article/details/81056399