問題描述
如下圖所示,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;
}
}