【蓝桥杯】真题训练 2013年C++A组 题9 剪格子

剪格子 

我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44566432/article/details/115178585