题目背景
1997年普及组第一题
题目描述
有一个 n×m 方格的棋盘,求其方格包含多少正方形、长方形(不包含正方形)。
输入格式
一行,两个正整数 n,mn,m(n \leq 5000,m \leq 5000n≤5000,m≤5000)。
输出格式
一行,两个正整数,分别表示方格包含多少正方形、长方形(不包含正方形)。
输入输出样例
输入 #1复制
2 3
输出 #1复制
8 10
思路简介:
此类有限解的题通过枚举来做比较简单粗暴,难点是枚举题的三步走:
一、找到合适的枚举变量,合适便是指,当有多个可能的枚举变量时,思考能否简化枚举量,往往结合数学思想,一般有几个枚举变量便有几重循环;
二、确定枚举变量的范围;
三、确定如何对枚举变量进行操作和判断。
比如此处,有多种枚举变量可以选择,如棋盘中的点,线等等。但显然此处可以利用数学公式大大优化思路。棋盘中的矩形个数显然是好求的,利用高中数学的排列组合。矩形有四条边,两横两竖。棋盘由n*m个小方格组成,有n+1竖线,m+1横线。分别从竖线中选2条(与顺序无关)所以总的矩形个数就是。
而显然矩形除了正方形就是长方形,即矩形个数=正方形个数+长方形个数。且正方形要求四边相等,所以更加好求。用矩形的个数减去正方形的个数显然就得到了长方形的个数。
那怎么求正方形个数呢?可利用正方形的四边长相等,那怎么确定枚举变量呢?是看边还是点呢?枚举题要尽量的思考如何优化枚举变量和对枚举变量的判断和操作。对于此题,在棋盘中,我们可利用以正方形的边长的长度作为枚举变量,正方形的边长显然是从1开始,而最大边长取决于min{n,m}。这样一分析,我们就完成了解枚举题的第一步和第二步,即找到合适的枚举变量和其范围。最重要也是最“麻烦”的第三步,便是确定如何对枚举变量进行操作和判断。
在此处,“难点”便是如何确定边长为确定值时在矩形中有多少个正方形。我们都知道,正方形也只不过是特殊的矩形,既然我们前面在利用竖线x横线的方式确定矩形的个数,我们同样也可以利用此方法来确定当边长确定时正方形的个数。也可以其他方法,比如,矩形显然也是由矩形构成的,最小的矩形便是只有一条边的正方形,所以显然可以用递归,一层层递归。其实也可以用公式法,对于解决这个问题,有个公式需要知道,假设一个矩阵长为5,宽为4,那么求一个矩形中的正方形个数公式为:4*5+【(4-1)(5-1)】+【(4-2)(5-2)】+【(4-3)(5-3)】,因为(4-4)*(5-4)是0,没实际意义,所以不需要再继续。所以i的范围是1到min{n,m}。
AC代码如下
#include<stdio.h>
long m,n,x,y;//在32位编译系统int和long都是占4字节,但64位long是占8字节,洛谷的编译系统应该是64位的。变量都开long一来是为了压行,二来是为了防止在计算矩形个数时数据溢出。
long min(long a,long b) {
return a<b?a:b;
}
int main() {
scanf("%ld%ld",&m,&n);
x=((m+1)*m/2)*((n+1)*n/2);//x记录矩形的总个数
for(long i=1; i<= min(m,n); i++) {//i作为枚举变量,代表正方形的边长
y+=(m-i+1)*(n-i+1);//y记录正方形的个数
}
printf("%ld %ld", y, x-y);//x-y便是长方形的个数
return 0;
}