题目
如下图所示,3 x 3 的格子中填写了一些整数(注意是整数!)。
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+
我们沿着图中的星号线剪开,得到两个部分,每个部分的数字和都是60。
本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0。
分析
因为如果有解答的话,要求输出包含左上角格子的那个区域包含的格子的最小数目,所以我们只需要从左上角的格子开始找符合要求的区域即可;
又因为要把m x n的格子分割为两个部分,使得这两个区域的数字和相等,所以两个区域的数字和等于总的数字和除以2。
说明:
总的数字和有两种情况,一是奇数,二是偶数
①奇数时,比如5, 5 ÷ 2 = 2.5 5÷2=2.5 5÷2=2.5,因为格子都是整数,所以找不到,输出0
②偶数时,可以找到。
我们采用从左上角的格子出发,做深搜;每次记录此次搜索的格子数,直到找到的格子里的数字和等于总数字和的一半,返回,如果此时的格子数少于之前,更新答案。
代码
#include<iostream>
#include<limits>
using namespace std;
int m, n, ans = INT_MAX, target; //m,n为格子的列数和行数,ans是区域包含的
//格子的最小数目,target等于格子数字总和的一半
int Next[4][2] = { {0,1},{1,0},{0,-1},{-1,0} }; //右下左上四个方向
int table[10][10]; //table数组为n×n大的格子,m,n最大不超过10(题目规定的范围)
bool book[10][10]; //table的标记数组,一开始里面全是false
//深搜
void findMin(int now,int x,int y,int step) { //now为当前搜索的格子里的数的和,
//x、y为坐标(左上为0.0),
//step为当前深搜里有的格子数
//返回条件
if (now == target) {
if (ans > step) //更新ans为当前找到的符合要求所用的最小格子数
ans = step;
return;
}
if (now > target || step > ans)
return;
//尝试四个方向(常规的深搜操作)
for (int i = 0; i < 4; i++) {
int nx, ny;
nx = x + Next[i][0];
ny = y + Next[i][1];
if (nx < 0 || ny < 0 || nx >= n || ny >= m || book[nx][ny] == true)
continue;
book[nx][ny] = true;
findMin(now + table[nx][ny], nx, ny, step + 1);
book[nx][ny] = false;
}
}
int main() {
ios::sync_with_stdio(false); //只用了cin和cout输入输出的
//加上这句可一定程度地加快速度
//输入格子
cin >> m >> n;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
cin >> table[i][j];
target += table[i][j];
}
target /= 2; //初始化target
book[0][0] = true; //标记左上角这个格子,为深搜做准备
findMin(table[0][0], 0, 0, 1);
//按要求输出
if (ans != INT_MAX)
cout << ans;
else
cout << 0;
return 0;
}