题目描述
问题描述
如下图所示,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。
输出格式
输出一个整数,表示在所有解中,包含左上角的分割区可能包含的最小的格子数目。
样例输入
3 3
10 2 52
20 30 1
1 2 3
样例输出
3
算法分析
-
首先要注意的是,题目输入的是先输m再输n,但是是n行m列的,一定要看清楚要求的问题!那些错误25%的代码问题大都在这。
-
对于该问题,首先对所有数据求和,最后问题变为寻找到一块连在一块的数据之和等于所有数据的一半即可。典型的dfs问题,记得回溯。
-
那么怎么求最少格子数呢,我的思路是先从(0,0)开始,假设答案为1,若不可,将答案+1,继续尝试。因为本题解答树深度不会超过100,所以一定能在有限的深度里找到答案。
-
接下来是非常非常重要的一点,大家看一下这组数据:
1. 5 10 15
2. 7 1 2
3. 3 3 34
其实答案应该为5(左边加上边),这块数据被(0,0)点分割成两块,dfs无法探索出这样的路径,可再写一个函数用于这种特殊情况的判断,只需maxd-2次判断即可。但由于蓝桥和该网站的数据不够全面,未考虑到这种情况的代码也是正确的,所以我也未完善自己的代码,主要是因为菜和懒。
参考代码
#include<iostream>
#include<cstring>
#define N 100
using namespace std;
int board[N+5],m,n; //n行m列
int vis[N+5];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int solve_sum=0;
int sum(int a[],int n) //求和函数
{
int i,sum=0;
for(i=0;i<n;++i) sum+=a[i];
return sum;
}
//从(m1,n1)开始探索k步,如果达到goal,则返回ture
bool solve(int goal,int m1,int n1,int k){
int i = m1*m + n1;
int j;
solve_sum += board[i];
vis[i]=1;
if(solve_sum == goal) return true;
if(k==0 || solve_sum>goal) return false; //剪枝
for(j=0;j<4;++j){ //未达目标或未走完
int idx=(m1+dx[j])*m + n1+dy[j];
if(m1+dx[j]>-1 && m1+dx[j]<n
&& n1+dy[j]>-1 && n1+dy[j]<m && !vis[idx])
{
if(solve(goal,m1+dx[j],n1+dy[j],k-1)) return true;
vis[idx] = 0;
solve_sum -= board[idx];
}
}
return false;
}
int main()
{
int i,maxd=1;
cin>>m>>n;
for(i=0;i<m*n;++i) cin>>board[i];
int s,goal;
s=sum(board,m*n);
if(s&1) cout<<0; //奇数一定无法分割
else{
goal = s/2;
while(maxd<m*n){ //主循环
memset(vis,0,sizeof(vis));
if(solve(goal,0,0,maxd-1)){
cout<<maxd;
break;
}
maxd++;
solve_sum=0; //清零
}
}
if(maxd==m*n) cout<<0; //未找到
return 0;
}