问题描述
如下图所示,3 x 3 的格子中填写了一些整数。
+--*--+--+
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+
我们沿着图中的星号线剪开,得到两个部分,每个部分的数字和都是60。
本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0。
输入格式
程序先读入两个整数 m n 用空格分割 (m,n<10)。
表示表格的宽度和高度。
接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000。
输出格式
输出一个整数,表示在所有解中,包含左上角的分割区可能包含的最小的格子数目。
样例输入1
3 3
10 1 52
20 30 1
1 2 3
样例输出1
3
样例输入2
4 3
1 1 1 1
1 30 80 2
1 1 1 100
样例输出2
10
思路分析:从题目和两个案例可以看出,本题是典型的dfs(深度优先搜索)算法的题目。采用dfs+回溯的思路,从数组左上角即(0,0)开始搜索,累加当前数字,直到数字和为总数字和的一半,返回走到数组当前位置的步数。值得说明的是,本题先读入的m是列,而不是n(行)。具体思路请看代码,附带详细注释。
import java.util.Scanner;
/**
*
* Created with MyEclipse
*
* @Description 蓝桥杯 历届试题 PREV-4 剪格子
* @author Jun
* @date 2020年4月16日
*/
public class Problem04 {
static int[][] dir = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } }; // 方向右,下,左,上
static boolean[][] user;
static int[][] map;
static n, m, ans = Integer.MAX_VALUE, sum = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
m = scan.nextInt();
n = scan.nextInt();
user = new boolean[n][m];
map = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = scan.nextInt();
sum += map[i][j];
}
}
if (sum % 2 == 0) { // 和为偶数
// for (int i = 0; i <= n/2; i++) {
// for (int j = 0; j < m/2; j++) {
// user[i][j]=true;
// int temp = dfs(i, j, map[i][j]);
// user[i][j]=false;
// if (temp != 0) { // 获取所有结果中的最小值
// ans = Math.min(ans, temp);
// }
//
// }
// }
user[0][0] = true;
System.out.println(dfs(0, 0, map[0][0]));
// System.out.println(ans);
} else {
System.out.println(0);
}
}
public static boolean checkNext(int x, int y, int num) { // 检查(x,y)是否允许走
if (x < 0 || x >= n || y < 0 || y >= m) { // 数组越界
return false;
}
if (user[x][y]) { // 该位置元素操作过
return false;
}
if (num + map[x][y] > sum / 2) { // 走这一步超过了和的一半
return false;
}
return true;
}
public static int dfs(int x, int y, int num) { // 代表搜索完点(x,y)时左上角的和sum
if (num == sum / 2) {
return 1;
}
for (int i = 0; i < 4; i++) {
int nx = x + dir[i][0], ny = y + dir[i][1]; // 下一个点的坐标
if (!checkNext(nx, ny, num)) { // 下一个点不可以走
continue;
}
// 下一步可以走
user[nx][ny] = true;
int res = dfs(nx, ny, num + map[nx][ny]); //dfs,递归探查下一个点
if (res != 0) { // 产生结果,直接返回
return res + 1;
}
user[nx][ny] = false; //回溯
}
return 0;
}
}