求点阵中可构成正方形最大数量(C++实现)

如图所示:求图中顶点作为正方形的顶点时,可构成正方形的最大数量为多少?

注意

以下两种情况都需要考虑到!

以下提供我的设计思路:

1.不难想到,我们可以把上图的点阵建模成cols*rows的矩阵,其中用0表示空隙,用1表示待求顶点

2.设计主循环执行矩阵(cols-1)*(rows-1)的遍历(因为之所以能构成正方形,A点发射出B点必有dx >=1,dy>=1),将改问题转化为当前每个遍历点(x,y)所能构成正方形个数的问题

int RectangleNums(int* arr, int rows, int cols) {
	int RectangleCount = 0;
	for(int i = 0; i < rows - 1; i++) {
		for(int j = 0; j < cols - 1; j++) {
			if(*(arr + i * cols + j)) {
				RectangleCount = RectangleCount + tRectangleNums(arr, j, i, rows, cols);
			}
		}
	}
	return RectangleCount;
}

3.设计主循环内部的子算法:

判别该点可构成多少个正方形(其中分为上图2种判别策略,①不旋转正方形(旋转角a=0°),②顺时针旋转正方形(0°<a<90°),因为一个象限为一个旋转周期,超过会造成判别重复)

        ①旋转:根据简单几何推断,易得:1<=dx<=cols-x-1,1<=dy<=rows-y-1

        ②非旋转:同理,易得:1<=dx<=cols-x-1,dy == 0

  4.通过该特征,设计判别可构成的正方形:

        ①是否超出画布

        ②是否4个点都落在有效顶点上       

         即可,

        由几何特征易得:

                ①旋转:A(x, y)、B(x+dx, y+dy)、C(x+dx-dy, y+dx+dy)、D(x-dy, y+dx)

                ②非旋转:A(x, y)、B(x+dx, y)、C(x+dx, y+dx)、D(x, y+dx),      

注意:要考虑规避造成以A点为当前枚举点和以B点为当前枚举点的时候是同一个正方形的重复组合情况的bug(若将4个顶点标定为A、B、C、D)

代码为:

int tRectangleNums(int* arr, int x, int y, int rows, int cols) {
	int Nums = 0;
	for(int dx = 0; dx <= cols - x - 1; dx++) {
		for(int dy = 0; dy <= rows - y - 1; dy++) {
			//判断分区一:旋转
			if(dx != 0 && dy != 0) {
				if(x - dy < 0 || y + dx + dy >= rows) {
					break;
				}
				if(x - dy >= 0 && y + dx < rows && x + dx - dy < cols && y + dx + dy < rows) {
					if(*(arr + (x + dx) + (y + dy) * cols) && *(arr + (x + dx - dy) + (y + dx + dy) * cols) && *(arr + (x - dy) + (y + dx) * cols)) {
						Nums++;
						std::cout << "[" << x << "]" << "[" << y << "] " <<
							"[" << x + dx << "]" << "[" << y + dy << "] " <<
							"[" << x + dx - dy << "]" << "[" << y + dx + dy << "] " <<
							"[" << x - dy << "]" << "[" << y + dx << "] " << std::endl;
					}
				}
			} else {
				//判断分区二:非旋转
				if(dx != 0 && dy == 0) {
					if(y + dx >= rows) {
						break;
					}
					if(*(arr + (x + dx) + y * cols) && *(arr + (x + dx) + (y + dx) * cols) && *(arr + x + (y + dx) * cols)) {
						Nums++;
						std::cout << "[" << x << "]" << "[" << y << "] " <<
							"[" << x + dx << "]" << "[" << y << "] " <<
							"[" << x + dx << "]" << "[" << y + dx << "] " <<
							"[" << x << "]" << "[" << y + dx << "] " << std::endl;
					}
				}
			}
		}
	}
	return Nums;
}

注意:循环设计成递增式,以及上述增加的break语句块,可以优化搜索速度!!

以下提供main函数:

int main() {
	int arr[ ][6] = {
		{ 0, 0, 1, 1, 0, 0 },
		{ 0, 0, 1, 1, 0, 0 },
		{ 1, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 1 },
		{ 0, 0, 1, 1, 0, 0 },
		{ 0, 0, 1, 1, 0, 0 },
	};
	int rows = sizeof(arr) / sizeof(arr[0]);
	int cols = sizeof(arr[0]) / sizeof(arr[0][0]);

	std::cout << "待计算的矩阵(0代表空隙,1代表顶点):" << std::endl;
	for(int i = 0; i < rows; i++) {
		for(int j = 0; j < cols; j++) {
			std::cout << arr[i][j] << " ";
		}
		std::cout << std::endl;
	}
	std::cout << std::endl;

	int* ptr = (int*)arr;
	int counts = RectangleNums(ptr, rows, cols);

	std::cout << "可构成正方形的个数:" << std::endl << counts << std::endl;
}

再仔细思考会发现,代码中存在冗余:

不旋转的正方形其实也是特殊的旋转正方形,

旋转:A(x, y)、B(x+dx, y+dy)、C(x+dx-dy, y+dx+dy)、D(x-dy, y+dx)

②非旋转:A(x, y)、B(x+dx, y)、C(x+dx, y+dx)、D(x, y+dx),      

非旋转时:  令dy = 0即可

因此代码可去掉不旋转的判断分区:

#include <iostream>

int tRectangleNums(int* arr, int x, int y, int rows, int cols) {
	int Nums = 0;
	for(int dx = 0; dx <= cols - x - 1; dx++) {
		for(int dy = 0; dy <= rows - y - 1; dy++) {
			if(dx != 0) {
				if(x - dy < 0 || y + dx + dy >= rows) {
					break;
				}
				if(x - dy >= 0 && y + dx < rows && x + dx - dy < cols && y + dx + dy < rows) {
					if(*(arr + (x + dx) + (y + dy) * cols) && *(arr + (x + dx - dy) + (y + dx + dy) * cols) && *(arr + (x - dy) + (y + dx) * cols)) {
						Nums++;
						std::cout << "[" << x << "]" << "[" << y << "] " <<
							"[" << x + dx << "]" << "[" << y + dy << "] " <<
							"[" << x + dx - dy << "]" << "[" << y + dx + dy << "] " <<
							"[" << x - dy << "]" << "[" << y + dx << "] " << std::endl;
					}
				}
			}
		}
	}
	return Nums;
}
int RectangleNums(int* arr, int rows, int cols) {
	int RectangleCount = 0;
	for(int i = 0; i < rows - 1; i++) {
		for(int j = 0; j < cols - 1; j++) {
			if(*(arr + i * cols + j)) {
				RectangleCount = RectangleCount + tRectangleNums(arr, j, i, rows, cols);
			}
		}
	}
	return RectangleCount;
}
int main() {
	int arr[ ][6] = {
		{ 0, 0, 1, 1, 0, 0 },
		{ 0, 0, 1, 1, 0, 0 },
		{ 1, 1, 1, 1, 1, 1 },
		{ 1, 1, 1, 1, 1, 1 },
		{ 0, 0, 1, 1, 0, 0 },
		{ 0, 0, 1, 1, 0, 0 },
	};
	int rows = sizeof(arr) / sizeof(arr[0]);
	int cols = sizeof(arr[0]) / sizeof(arr[0][0]);

	std::cout << "待计算的矩阵(0代表空隙,1代表顶点):" << std::endl;
	for(int i = 0; i < rows; i++) {
		for(int j = 0; j < cols; j++) {
			std::cout << arr[i][j] << " ";
		}
		std::cout << std::endl;
	}
	std::cout << std::endl;

	int* ptr = (int*)arr;
	int counts = RectangleNums(ptr, rows, cols);

	std::cout << "可构成正方形的个数:" << std::endl << counts << std::endl;
}

运行效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_xian_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值