剪格子
我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60。
本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0程序输入输出格式要求:
程序先读入两个整数 m n 用空格分割 (m,n<10)
表示表格的宽度和高度
接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000
程序输出:在所有解中,包含左上角的分割区可能包含的最小的格子数目。例如:
用户输入:
3 3
10 1 52
20 30 1
1 2 3则程序输出:
3再例如:
用户输入:
4 3
1 1 1 1
1 30 80 2
1 1 1 100则程序输出:
10
题目解析:
递归+回溯+剪枝
思路:对格子进行深度搜索,用到的还是递归,那么变化状态就是每次的坐标,以及记录的总和,count用来记录走的步数;出口,就是当走过格子的总数值超过所有总和一半时就可以直接返回了,而当整好等于总和一半时,需要比较一下这次达到一半的步数与之前步数相比是不是最小的;相同状态的递归调用就是,每一步都有四个方向的,对四个方向的格子再递归进行调用,对判断的条件也是需要注意的。
需要注意的是,一般来说,对有图遍历考虑到不能重复走原来的路(不能重复遍历时),需要另外申请一片空间用来记录是否访问过该地址,即重新开辟vis数组,未访问过的为0,访问过的为1。
#include <iostream>
#include <algorithm>
using namespace std;
int m, n;
int g[10][10];
int vis[10][10]; //记录走过的状态
int total;
int res = 1000;
int count;
void f(int i, int j, int sum, int count){ //变化状态 ->参数
//边界,出口条件,当sum和大于总的一半时可以返回
if(sum > total / 2){
return ;
}
if(sum == total / 2){ //当sum 等于总的一半时,可以剪了,那么把这里
res = min(res, count);
return;
}
vis[i][j] = 1; //该格子被访问过了
//下一步的四个分支
if(i+1 <= n-1 && vis[i+1][j] == 0){ //保证下一步的格子没有走过
f(i+1, j, sum+g[i][j], count+1);
}
if(i-1 >= 0 && vis[i-1][j] == 0){
f(i-1, j, sum+g[i][j], count+1);
}
if(j+1 <= m-1 && vis[i][j+1] == 0){
f(i, j+1, sum+g[i][j], count+1);
}
if(j-1 >= 0 && vis[i][j-1] == 0){
f(i, j-1, sum+g[i][j], count+1);
}
vis[i][j] = 0;
}
int main(int argc, char** argv) {
cin >> m >> n;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> g[i][j];
total += g[i][j];
}
}
f(0, 0, 0, 0);
if(res != 1000){
cout << res;
}else{
cout << "0";
}
return 0;
}