历届试题 剪格子
时间限制: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,希望能将代码分享一下,٩( ‘ω’ )و 蟹蟹!。