如图所示:求图中顶点作为正方形的顶点时,可构成正方形的最大数量为多少?
注意:
以下两种情况都需要考虑到!
以下提供我的设计思路:
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;
}
运行效果: