面积计算
类似题目:
洛谷P1162 填涂颜料 https://www.luogu.org/problemnew/show/P1162
填涂颜料 题解https://www.cnblogs.com/huashanqingzhu/p/10744207.html
注意:本文所讲题目原为教材里面的练习题,当时的测试数据较弱,无法检测出本文所讲算法的bug。
下面的讲解仅提供一个参考思路激发思维。
正确的思路应该是在矩形外围加一个圈,全部设为0.从这个圈去搜索访问围墙外的那些0.
如下图所示:白色块是原数据的0,黄色块是原数据的围墙1。绿色块是我们为解决这道题,额外添加的一圈0.从左上角的绿色块出发做搜索,可以把围墙外的所有0都访问完。然后统计棋盘里面的0扫描、统计一下个数即可。
编程计算由“*”号围成的下列图形的面积。面积计算方法是统计*号所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在10*10的二维数组中,有“*”围住了15个0,因此面积为15。
首先输入m和n表示二维数组的行和列数目,然后输入m*n的二维数组,其中的*用1表示。
0 0 0 0 0 0 0 0 0 0
0 0 0 0 * * * 0 0 0
0 0 0 0 * 0 0 * 0 0
0 0 0 0 0 * 0 0 * 0
0 0 * 0 0 0 * 0 * 0
0 * 0 * 0 * 0 0 * 0
0 * 0 0 * * 0 * * 0
0 0 * 0 0 0 0 * 0 0
0 0 0 * * * * * 0 0
0 0 0 0 0 0 0 0 0 0
【样例输入】area.in
10 10
0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 0 0 0
0 0 0 0 1 0 0 1 0 0
0 0 0 0 0 1 0 0 1 0
0 0 1 0 0 0 1 0 1 0
0 1 0 1 0 1 0 0 1 0
0 1 0 0 1 1 0 1 1 0
0 0 1 0 0 0 0 1 0 0
0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0
【样例输出】area.out
15
算法思路1:
参考:http://blog.csdn.net/harlow_cheng/article/details/51889928?locationNum=9
把1看成围墙,我们是想进入围墙的人,但是怎么也进不了,于是我们就在围墙的周围乱走,把能不进围墙就走到的地方走个遍。然而她就是这么无懈可击,我也真是醉了。然后删掉围墙。剩下的就是被围住的东西了,看得到,但是永远得不到,因为它们被围起来了。从每个角落都进行一次广搜,找连通块,找到了置为false就可以了。
说这么多啰嗦的废话,其实是这么个意思:
从左上角开始一行一行地扫描二维数组,对遇到的每一个0都做一次BFS,搜过的点标记为1。但是要注意:对每一行扫描时,若是遇到了围墙,就立即停止对这一行的扫描,接着扫描下一行。这样一来就可以把“围墙左侧”连通的0都变为1。
然后:从右下角开始对二维数组的每一行从右向左进行扫描,遇到0则做BFS,搜过的点标记1。同样地,每一行扫描时遇到围墙则该行扫描结束,进入下一行的扫描。
这样一来可以使得“围墙右侧”连通的0变为1。
最后:经过上面两个环节以后,数组中剩余的0应该都是围墙内的0。所以,扫描一遍二维数组,统计0的个数就是答案。
这个算法可以解决曲线(围墙)比较简单的情况。但是足以满足题目要求。
代码如下:
1 #include <iostream> 2 #include<queue> 3 #include<stdio.h> 4 using namespace std; 5 6 #define localTest 1 7 8 #define maxM 103 9 #define maxN 103 10 11 int m,n,a[maxM][maxN]={0}; 12 int dx[4]={-1,0,1,0};//上右下左 13 int dy[4]={0,1,0,-1}; 14 15 void BFS(int x,int y); 16 17 int main() 18 { 19 int i,j,ans=0; 20 freopen("area_data/area5.in","r",stdin); 21 scanf("%d%d",&m,&n); 22 #ifdef localTest 23 printf("%d %d\n",m,n);// 24 #endif // localTest 25 for(i=0;i<m;i++) 26 { 27 for(j=0;j<n;j++) 28 { 29 scanf("%d",&a[i][j]); 30 #ifdef localTest 31 printf("%d ",a[i][j]);// 32 #endif // localTest 33 } 34 #ifdef localTest 35 printf("\n");// 36 #endif // localTest 37 } 38 39 for(i=0;i<m;i++) 40 { 41 for(j=0;j<n;j++) 42 { 43 if(a[i][j]==1) break; 44 if(a[i][j]==0) 45 { 46 BFS(i,j); 47 } 48 } 49 } 50 for(i=m-1;i>=0;i--) 51 { 52 for(j=n-1;j>=0;j--) 53 { 54 if(a[i][j]==1) break; 55 if(a[i][j]==0) 56 { 57 BFS(i,j); 58 } 59 } 60 } 61 62 #ifdef localTest 63 printf("--------------\n");// 64 #endif // localTest 65 for(i=0;i<m;i++) 66 { 67 for(j=0;j<n;j++) 68 { 69 if(a[i][j]==0)ans++; 70 #ifdef localTest 71 printf("%d ",a[i][j]);// 72 #endif // localTest 73 } 74 #ifdef localTest 75 printf("\n"); 76 #endif // localTest 77 } 78 printf("%d\n",ans); 79 return 0; 80 } 81 void BFS(int x,int y) 82 { 83 queue<int> qx,qy; 84 int xx,yy,i; 85 86 qx.push(x); 87 qy.push(y); 88 a[x][y]=1; 89 while(!qx.empty()) 90 { 91 for(i=0;i<4;i++) 92 { 93 xx=qx.front()+dx[i]; 94 yy=qy.front()+dy[i]; 95 if(xx>=0&&xx<m&&yy>=0&&yy<n&&a[xx][yy]==0) 96 { 97 a[xx][yy]=1; 98 qx.push(xx); qy.push(yy); 99 } 100 } 101 qx.pop(); qy.pop(); 102 } 103 }
算法思路2:
参考:http://blog.csdn.net/u011123263/article/details/17249283
把边界的0全部赋值为-1,然后进行两次遍历,从上往下,在从下往上;
在遍历时,若当前位置为0,如果上,下,左,右有一个是-1,则把当前的0更改为-1;
遍历完后,再统计0的个数,就是所求的结果。
这个算法思路与算法思路1其实差不多,但是实现起来比较简单。
代码:
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 5 #define localTest 1 6 7 #define maxM 103 8 #define maxN 103 9 10 int m,n,a[maxM][maxN]={0}; 11 int dx[4]={-1,0,1,0};//上右下左 12 int dy[4]={0,1,0,-1}; 13 14 int main() 15 { 16 int i,j,ans=0; 17 int k,xx,yy; 18 freopen("area_data/area5.in","r",stdin); 19 scanf("%d%d",&m,&n); 20 #ifdef localTest 21 printf("%d %d\n",m,n); 22 #endif // localTest 23 for(i=0;i<m;i++) 24 { 25 for(j=0;j<n;j++) 26 { 27 scanf("%d",&a[i][j]); 28 #ifdef localTest 29 printf("%2d ",a[i][j]); 30 #endif // localTest 31 } 32 #ifdef localTest 33 printf("\n"); 34 #endif // localTest 35 } 36 37 for(i=0;i<m;i++)//把第0列和最后一列的0变为-1 38 { 39 if(a[i][0]==0) a[i][0]=-1; 40 if(a[i][n-1]==0) a[i][n-1]=-1; 41 } 42 for(j=0;j<n;j++)//把第0行和最后一行的0变为-1 43 { 44 if(a[0][j]==0) a[0][j]=-1; 45 if(a[m-1][j]==0) a[m-1][j]=-1; 46 } 47 48 for(i=1;i<m-1;i++) 49 { 50 for(j=1;j<n-1;j++) 51 { 52 if(a[i][j]==0) 53 { 54 for(k=0;k<4;k++) 55 { 56 xx=i+dx[k]; yy=j+dy[k]; 57 if(xx>=0&&xx<m&&yy>=0&&yy<n&&a[xx][yy]==-1) 58 { 59 a[i][j]=-1; 60 break; 61 } 62 } 63 } 64 } 65 } 66 67 for(i=m-1;i>=0;i--) 68 { 69 for(j=n-1;j>=0;j--) 70 { 71 if(a[i][j]==0) 72 { 73 for(k=0;k<4;k++) 74 { 75 xx=i+dx[k]; yy=j+dy[k]; 76 if(xx>=0&&xx<m&&yy>=0&&yy<n&&a[xx][yy]==-1) 77 { 78 a[i][j]=-1; 79 break; 80 } 81 } 82 } 83 } 84 } 85 86 #ifdef localTest 87 printf("\n--------------\n"); 88 #endif // localTest 89 for(i=0;i<m;i++) 90 { 91 for(j=0;j<n;j++) 92 { 93 if(a[i][j]==0)ans++; 94 #ifdef localTest 95 printf("%2d ",a[i][j]); 96 #endif // localTest 97 } 98 #ifdef localTest 99 printf("\n"); 100 #endif // localTest 101 } 102 printf("%d\n",ans); 103 return 0; 104 }
后续:
为何要从左上角、右下角做两次的扫描呢?这个主要是考虑到有一些特殊情况下的输入。不多说,看下面这两组输入:
左边这组特殊的输入,在代码一当中,假如没有从右下角扫描处理“”围墙右侧”的0,则统计结果会多出一些0。所以需要从右下角再做一次扫描。
右侧这一组特殊的输入,在代码二中,假如只做左上角开始的扫描,那么中间那几个0会因为在检测到它们的时候,周围没有-1而保持0的值。所以要从右下角再做一次扫描。