蓝桥杯 历届试题 PREV-4 剪格子 搜索

历届试题 剪格子
时间限制:1.0s 内存限制:256.0MB
问题描述
如下图所示,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

分析:
  1. 虽然输入是m,n,但实际是n行m列。
  2. 分割的两个区域数字和相等,所以就可以看做:当包含左上角格子的区域数字和为所有格子总和的一半时,就符合条件-。
  3. 既然是包含左上角的分割区,我们检索格子的时候,就可以从左上角的格子开始。
  4. 本题采用回溯法,从左上角的格子开始,依次加下一个格子中的数值,当区域和为总和的一半时,记录格子的数量,取所有可能中格子数量最少的值为最终结果。
  
代码如下:

#include <iostream>
#include <vector>
using namespace std;

//格子结构体  
struct grid
{
	int data;		//存数据 
	bool visit;		//标记该数据是否被访问
};

int n, m;
grid **num;
int sum = 0;
int res = 100;	//格子数量小于10*10,所以初始化为100防止干扰

//回溯法:x,y为格子坐标,value为当前区域数字的和,count为当前区域格子的数量
void fun(int x, int y, int value, int count)
{
	value += num[x][y].data;
	if(value == sum / 2)	//满足条件 
	{
		if(res > count + 1) res = count + 1;	//取格子较少的情况 
		return;
	}
	
	if(value > sum) return;	//不满足条件
	
	if(x - 1 >= 0 && num[x-1][y].visit == false)	//上 
	{
		num[x-1][y].visit = true;
		fun(x-1, y, value, count+1);
		num[x-1][y].visit = false;
	}
	
	if(x + 1 < n && num[x+1][y].visit == false)		//下 
	{
		num[x+1][y].visit = true;
		fun(x+1, y, value, count+1);
		num[x+1][y].visit = false;
	}
	
	if(y - 1 >= 0 && num[x][y-1].visit == false)	//左 
	{
		num[x][y-1].visit = true;
		fun(x, y-1, value, count+1);
		num[x][y-1].visit = false;
	}
	
	if(y + 1 < m && num[x][y+1].visit == false)		//右 
	{
		num[x][y+1].visit = true;
		fun(x, y+1, value, count+1);
		num[x][y+1].visit = false;
	}
}

int main()
{
	//输入
	cin >> m >> n;
	//创建格子并输入数据
	num = new grid*[n];
	for(int i = 0; i < n; i++)
	{
		num[i] = new grid[m];
		for(int j = 0; j < m; j++)
		{
			cin >> num[i][j].data;
			num[i][j].visit = false;	//初始化未被访问
			sum += num[i][j].data;		//计算所有格子的总和
		}
	}
	
	if(sum % 2) cout << 0 << endl;	//如果sum不能被2整除,则无结果,输出0
	else
	{	//从num[0][0],即左上角的格子开始
		num[0][0].visit = true;
		fun(0, 0, 0, 0); 
		if(res == 100) res = 0;	//如果res还是初值,说明无结果,输出0 
		cout << res << endl;		//输出最终结果
	}
	return 0;
}

PS:这里有一个bug,就是题说是包含左上角,并没有说左上角的格子是区域中的边界,所以当输入为:
3 3
2 2 1
2 1 1
1 1 1
时,我这个代码的输出为4,即2 2 1 1的情况下,四个格子。
而实际应该是三个2相连的区域,输出为3,如下图:

+-----*--+
| 2| 2* 1|
+--****--+
| 2* 1| 1|
****--+--+
| 1| 1| 1|
+--+--+--+

错就错在我是以左上角的格子为回溯法的开始,所以才会有这种情况。
但是我的代码在蓝桥杯上是100分,可能是检测的数据并不全面吧。
如果有朋友能解决以上这个bug,希望能将代码分享一下,٩( ‘ω’ )و 蟹蟹!。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值