活动地址:CSDN21天学习挑战赛
题目描述
给一个二维数组M行N列,数组中的数字代表图片的像素,为了简化问题,仅包含像素1和5两种像素,每种像素代表一个物体,将两个不同物体相邻的格子称为边界,求像素1代表的边界个数。(0<M<100,0<<100)
其中像素1代表的物体边界是指像素为1并且与像素5相邻的格子,而相邻的概念是指八个方向(上、下、左、右、左上、左下、右上、右下)的相近。
如上图,其中黄色部分即为边界,根据相邻的概念,黄色的格子组成了两个边界。
上图中的黄色格子据相邻的概念只有一个边界。
输入示例
6 6
1 1 1 1 1 1
1 5 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 5
输出
2
解析见上图
思考
解决这个问题可以分为两步:
-
确定边界格子
-
判断边界格子所属边界的个数
那么就有了解决问题的思路,第一步确定边界格子,可以将5周围的格子标记出来,这里是数组,那就赋一个不是1、5的值就好了,这里用2标记,于是我们有了:
2 2 2 1 1 1
2 5 2 1 1 1
2 2 2 1 1 1
1 1 1 1 1 1
1 1 1 1 2 2
1 1 1 1 2 5
这样所有的边界格子被标记出来了,然后第二步判断边界格子所属边界,那么就是找2的格子,找到一个2的格子a便立即根据相邻规律将所有与格子a相邻的所有为2的格子变为3,那么找到2的次数便是边界的个数。
3 3 3 1 1 1
3 5 3 1 1 1
3 3 3 1 1 1
1 1 1 1 1 1
1 1 1 1 3 3
1 1 1 1 3 5
代码
根据上面的思考,里面都有一个给相邻格子赋值的操作,先抽象成方法。
/**给相邻的格子赋值,pixel为当前值,target为目标值,只有当前值为pixel才会有赋值操作
*i,j分别对应数组中中心点的位置
*recursion确定是否为递归
*/
public void put(int i, int j, int[][] array, int pixel, int target, boolean recursion) {
//遍历上、中、下
for (int a=-1;a<=1;a++){
//遍历左、中、右
for (int b=-1;b<=1;b++) {
//确保数组下标不越界,并且数组当前位置的值为指定像素值pixel
if (i+a < array.length && i+a >= 0 &&
j+b < array[0].length && j+b >= 0 &&
array[i+a][j+b] == pixel) {
array[i+a][j+b] = target;
//递归
if (recursion) {
put(i+a,j+b, array, pixel, target, true);
}
}
}
}
}
此处定义的方法 put()
主要功能是将数组当前位置的所有相邻部分并且其像素pixel为指定值的格子赋上目标值target,当递归recursion为true时,会将根据相邻规则画出的一个块都赋上目标值。
第二步,判断边界个数,用上面的方法递归计算边界的个数。
public static void main(String[] args) {
//初始化数组
int[][] a = new int[6][6];
for (int i=0;i<a.length;i++) {
for (int j=0;j<a[0].length;j++){
a[i][j] = 1;
}
}
a[1][1] = 5;
a[5][5] = 5;
//标记像素5的边界,用2标记
for (int i=0;i<a.length;i++) {
for (int j=0;j<a[0].length;j++){
if (a[i][j] == 5)
//调用put方法标记边界
put(i,j,a,1,2,false);
}
}
//统计边界个数
int count = 0;
for (int i=0;i<a.length;i++) {
for (int j=0;j<a[0].length;j++){
//找到2的格子,并递归标记
if (a[i][j] == 2) {
put(i, j, a, 2, 3, true);
count ++;
}
}
}
System.out.println("边界个数:" + count);
}
其实这里还可以尝试用不同的值标记不同的边界,那么也许更明显,但是在尝试过程中,一定要避免2、1之类的已经出现的值,可能让递归陷入死循环,会报栈溢出错误。
至此,问题解决。