题目描述
绕线画是一种在木板上钉入图钉,然后用线在图钉之间缠绕,组成几何图形的艺术。
阿甘最近正在学习绕线画,但是由于是初学者,因此需要在木板上画一个网格坐标系,然后在特定坐标点上钉入图钉。
现在木板上有一些图钉,而阿甘只能缠绕出一些简单几何图形,比如正方形,请你帮助阿甘计算出基于现有图钉,能缠绕出多少种不同的正方形,并输出这些正方形四个点的坐标信息。
输入描述
输入一个二维数组,数组元素是坐标信息 [x, y],格式如下:
[[0, 0], [4, 4], [4, -4], [8, 0], [-4, 4], [0, 8]]
最多输入 200 个坐标。
输出描述
第一行输出阿甘可以缠绕出多少种不同正方形。
之后每行输出一种可以缠绕出的正方形的坐标信息。格式如下:
[[0, 0], [0, 8], [4, -4], [4, 4]]
顺序要求:
- 正方形内部坐标之间,都按照横坐标升序,若横坐标相同,则按照纵坐标升序。
- 不同正方形之间,按照首个坐标的横坐标升序,若横坐标相同,则按照纵坐标升序,若都相同,继续按相同逻辑比较下一个坐标。
用例
输入 | [[0, 0], [4, 4], [4, -4], [8, 0], [-4, 4], [0, 8]] |
输出 | 2 [[-4, 4], [0,0], [4, 4], [8, 0]] [[0, 0], [0, 8], [4, -4], [4, 4]] |
说明 | ![]() |
题目解析
其实当我们知道正方形相邻两点的坐标,即某条边的坐标后,就可以求出其余两点的坐标。
如下图中,我们知道正方形的红色点坐标后,就画出绿色点坐标和橙色点坐标来形成两个正方形,这其中似乎隐藏着什么规律?
我们选取其中一个正方形来分析
我们在目标正方形外面包裹一个更大的正方形,此时可以发现大正方形和小正方形相交点切割出了相同的几个尺寸:d1和d2。
假设已知A点坐标(x1, y1),B点坐标(x2,y2),那么
- d1 = x1 - x2
- d2 = y1 - y2
其实很容易可以发现,d1含义是A,B两点横向距离,d2是A,B两点纵向距离。
基于A,B点坐标,以及d1,d2,我们可以算出C,D点坐标分别为:
- C坐标 (x2 + d2, y2 - d1)
- D坐标 (x1 + d2, y1 - d1)
继续转化一下可得:
- C坐标 (x2 + y1 - y2, y2 - (x1 - x2))
- D坐标 (x1 + y1 - y2, y1 - (x1 - x2))
这是求A,B右下方向C,D边得公式推导。
同理,可以根据A,B推导出其左上方向E,F边,图示如下:
基于A,B点坐标,以及d1,d2,我们可以算出E,F点坐标分别为:
- E坐标 (x1 - d2,y1 + d1)
- F坐标 (x2 - d2,y2 + d1)
继续转化一下可得:
- E坐标 (x1 - (y1 - y2),y1 + x1 - x2)
- F坐标 (x2 - (y1 - y2),y2 + x1 - x2)
此时我们就得到了根据正方形任意相邻两点坐标,求另外两点坐标的公式了。
因此,接下来我们只需要遍历出两个点,然后通过公式得出另外可能的两个点,再在所有点中查找是否存在可能的两点,若存在,则正方形count++。
最后的正方形个数squareCount 需要除以 4,原因是,如果输入中真的存在如下图中的绿色,橙色点,则遍历过程中也会将绿色,橙色点遍历出来,然后求它们的可能正方形
也就是说上图中两个正方形,不仅会被两个红色点求出来两次次,还会被两个绿色点求出来一次,还会被两个橙色点求出来一次,还会被一绿一红求出来两次,被一橙一红求出来两次 ,总共是8次,而实际上只有2个正方形,因此最终结果要除以4。
JS算法源码
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
const coordinates = JSON.parse(await readline());
const set = new Set();
for (let [x, y] of coordinates) {
set.add(x + " " + y);
}
const squares = [];
for (let i = 0; i < coordinates.length; i++) {
let [x1, y1] = coordinates[i];
for (let j = i + 1; j < coordinates.length; j++) {
let [x2, y2] = coordinates[j];
let x3 = x1 - (y1 - y2);
let y3 = y1 + (x1 - x2);
let x4 = x2 - (y1 - y2);
let y4 = y2 + (x1 - x2);
if (set.has(x3 + " " + y3) && set.has(x4 + " " + y4)) {
squares.push([
[x1, y1],
[x2, y2],
[x3, y3],
[x4, y4],
]);
}
let x5 = x1 + (y1 - y2);
let y5 = y1 - (x1 - x2);
let x6 = x2 + (y1 - y2);
let y6 = y2 - (x1 - x2);
if (set.has(x5 + " " + y5) && set.has(x6 + " " + y6)) {
squares.push([
[x1, y1],
[x2, y2],
[x5, y5],
[x6, y6],
]);
}
}
}
console.log(squares.length / 4);
// 正方形内部坐标排序
squares.forEach((square) =>
square.sort(([x1, y1], [x2, y2]) => (x1 != x2 ? x1 - x2 : y1 - y2))
);
// 正方形之间排序
squares.sort((a, b) => {
for (let i = 0; i < 4; i++) {
const [x1, y1] = a[i];
const [x2, y2] = b[i];
if (x1 != x2) return x1 - x2;
else if (y1 != y2) return y1 - y2;
}
return 0;
});
// 去重打印
let pre = "";
for (let [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] of squares) {
const cur = `[[${x1}, ${y1}], [${x2}, ${y2}], [${x3}, ${y3}], [${x4}, ${y4}]]`;
if (cur != pre) {
console.log(cur);
pre = cur;
}
}
})();
Java算法源码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[][] coordinates = parse(sc.nextLine());
HashSet<String> set = new HashSet<>();
for (int[] coordinate : coordinates) {
int x = coordinate[0];
int y = coordinate[1];
set.add(x + " " + y);
}
ArrayList<int[][]> squares = new ArrayList<>();
for (int i = 0; i < coordinates.length; i++) {
int x1 = coordinates[i][0], y1 = coordinates[i][1];
for (int j = i + 1; j < coordinates.length; j++) {
int x2 = coordinates[j][0], y2 = coordinates[j][1];
int x3 = x1 - (y1 - y2), y3 = y1 + (x1 - x2);
int x4 = x2 - (y1 - y2), y4 = y2 + (x1 - x2);
if (set.contains(x3 + " " + y3) && set.contains(x4 + " " + y4)) {
squares.add(new int[][]{{x1, y1}, {x2, y2}, {x3, y3}, {x4, y4}});
}
int x5 = x1 + (y1 - y2), y5 = y1 - (x1 - x2);
int x6 = x2 + (y1 - y2), y6 = y2 - (x1 - x2);
if (set.contains(x5 + " " + y5) && set.contains(x6 + " " + y6)) {
squares.add(new int[][]{{x1, y1}, {x2, y2}, {x5, y5}, {x6, y6}});
}
}
}
System.out.println(squares.size() / 4);
for (int[][] square : squares) {
// 正方形内部坐标排序
Arrays.sort(square, (a, b) -> {
int x1 = a[0], y1 = a[1];
int x2 = b[0], y2 = b[1];
return x1 != x2 ? x1 - x2 : y1 - y2;
});
}
// 正方形之间排序
squares.sort((a, b) -> {
for (int i = 0; i < 4; i++) {
int x1 = a[i][0], y1 = a[i][1];
int x2 = b[i][0], y2 = b[i][1];
if (x1 != x2) return x1 - x2;
else if (y1 != y2) return y1 - y2;
}
return 0;
});
// 去重打印
String pre = "";
for (int[][] square : squares) {
StringJoiner sj = new StringJoiner(", ", "[", "]");
for (int i = 0; i < 4; i++) {
int x = square[i][0];
int y = square[i][1];
sj.add("[" + x + ", " + y + "]");
}
String cur = sj.toString();
if (!cur.equals(pre)) {
System.out.println(cur);
pre = cur;
}
}
}
public static int[][] parse(String s) {
String[] subs = s.substring(1, s.length() - 2).split("], ");
return Arrays.stream(subs).map(sub -> Arrays.stream(sub.substring(1).split(", ")).mapToInt(Integer::parseInt).toArray()).toArray(int[][]::new);
}
}
Python算法源码
if __name__ == '__main__':
coordinates = eval(input())
n = len(coordinates)
coordinatesSet = set()
for x, y in coordinates:
coordinatesSet.add(f"{x} {y}")
squares = []
for i in range(n):
x1, y1 = coordinates[i]
for j in range(i + 1, n):
x2, y2 = coordinates[j]
x3 = x1 - (y1 - y2)
y3 = y1 + (x1 - x2)
x4 = x2 - (y1 - y2)
y4 = y2 + (x1 - x2)
if f"{x3} {y3}" in coordinatesSet and f"{x4} {y4}" in coordinatesSet:
squares.append([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
x5 = x1 + (y1 - y2)
y5 = y1 - (x1 - x2)
x6 = x2 + (y1 - y2)
y6 = y2 - (x1 - x2)
if f"{x5} {y5}" in coordinatesSet and f"{x6} {y6}" in coordinatesSet:
squares.append([[x1, y1], [x2, y2], [x5, y5], [x6, y6]])
print(len(squares) // 4)
for square in squares:
# 正方形内部坐标排序
square.sort()
# 正方形之间排序
squares.sort()
# 去重打印
pre = ""
for square in squares:
x1, y1 = square[0]
x2, y2 = square[1]
x3, y3 = square[2]
x4, y4 = square[3]
cur = f"[[{x1}, {y1}], [{x2}, {y2}], [{x3}, {y3}], [{x4}, {y4}]]"
if cur != pre:
print(cur)
pre = cur
C算法源码
#include<stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define MAX_N 2000
int squares[MAX_N][4][2];
int squares_size = 0;
int cmp1(const void *a, const void *b) {
int *A = (int *) a;
int *B = (int *) b;
int x1 = A[0], y1 = A[1];
int x2 = B[0], y2 = B[1];
return x1 != x2 ? x1 - x2 : y1 - y2;
}
int cmp2(const void *a, const void *b) {
for (int i = 0; i < 4; i++) {
int x1 = (*(int (*)[4][2]) a)[i][0];
int y1 = (*(int (*)[4][2]) a)[i][1];
int x2 = (*(int (*)[4][2]) b)[i][0];
int y2 = (*(int (*)[4][2]) b)[i][1];
if (x1 != x2) {
return x1 - x2;
} else if (y1 != y2) {
return y1 - y2;
}
}
return 0;
}
int main() {
int coordinates[MAX_N][2];
int n = 0;
while (1) {
getchar();
getchar();
scanf("%d", &coordinates[n][0]);
getchar();
getchar();
scanf("%d", &coordinates[n][1]);
n++;
getchar();
if (getchar() != ',') break;
}
for (int i = 0; i < n; i++) {
int x1 = coordinates[i][0];
int y1 = coordinates[i][1];
for (int j = i + 1; j < n; j++) {
int x2 = coordinates[j][0];
int y2 = coordinates[j][1];
int x3 = x1 - (y1 - y2), y3 = y1 + (x1 - x2);
int x4 = x2 - (y1 - y2), y4 = y2 + (x1 - x2);
bool has3 = false;
bool has4 = false;
int x5 = x1 + (y1 - y2), y5 = y1 - (x1 - x2);
int x6 = x2 + (y1 - y2), y6 = y2 - (x1 - x2);
bool has5 = false;
bool has6 = false;
for (int k = 0; k < n; k++) {
if (x3 == coordinates[k][0] && y3 == coordinates[k][1]) {
has3 = true;
}
if (x4 == coordinates[k][0] && y4 == coordinates[k][1]) {
has4 = true;
}
if (x5 == coordinates[k][0] && y5 == coordinates[k][1]) {
has5 = true;
}
if (x6 == coordinates[k][0] && y6 == coordinates[k][1]) {
has6 = true;
}
}
if (has3 && has4) {
squares[squares_size][0][0] = x1;
squares[squares_size][0][1] = y1;
squares[squares_size][1][0] = x2;
squares[squares_size][1][1] = y2;
squares[squares_size][2][0] = x3;
squares[squares_size][2][1] = y3;
squares[squares_size][3][0] = x4;
squares[squares_size][3][1] = y4;
squares_size++;
}
if (has5 && has6) {
squares[squares_size][0][0] = x1;
squares[squares_size][0][1] = y1;
squares[squares_size][1][0] = x2;
squares[squares_size][1][1] = y2;
squares[squares_size][2][0] = x5;
squares[squares_size][2][1] = y5;
squares[squares_size][3][0] = x6;
squares[squares_size][3][1] = y6;
squares_size++;
}
}
}
printf("%d\n", squares_size / 4);
for (int i = 0; i < squares_size; i++) {
qsort(squares[i], 4, sizeof(squares[i][0]), cmp1);
}
qsort(squares, squares_size, sizeof(squares[0]), cmp2);
char pre[MAX_N] = {'\0'};
for (int i = 0; i < squares_size; i++) {
char cur[MAX_N] = {'\0'};
strcat(cur, "[");
for (int j = 0; j < 4; j++) {
int x = squares[i][j][0];
int y = squares[i][j][1];
char tmp[100] = {'\0'};
sprintf(tmp, "[%d, %d], ", x, y);
strcat(cur, tmp);
}
cur[strlen(cur) - 2] = ']';
cur[strlen(cur) - 1] = '\0';
if (strcmp(cur, pre) != 0) {
puts(cur);
strcpy(pre, cur);
}
}
return 0;
}
C++算法源码
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<vector<int>> coordinates;
set<string> coordinatesSet;
while (true) {
int x, y;
getchar();
getchar();
cin >> x;
getchar();
getchar();
cin >> y;
coordinates.emplace_back(vector<int>{x, y});
coordinatesSet.insert(to_string(x) + " " + to_string(y));
getchar();
if (getchar() != ',') break;
}
vector<vector<vector<int>>> squares;
for (int i = 0; i < coordinates.size(); i++) {
int x1 = coordinates[i][0];
int y1 = coordinates[i][1];
for (int j = i + 1; j < coordinates.size(); j++) {
int x2 = coordinates[j][0];
int y2 = coordinates[j][1];
int x3 = x1 - (y1 - y2), y3 = y1 + (x1 - x2);
int x4 = x2 - (y1 - y2), y4 = y2 + (x1 - x2);
if (coordinatesSet.count(to_string(x3) + " " + to_string(y3)) > 0 &&
coordinatesSet.count(to_string(x4) + " " + to_string(y4)) > 0) {
squares.emplace_back(vector<vector<int>>{{x1, y1},
{x2, y2},
{x3, y3},
{x4, y4}});
}
int x5 = x1 + (y1 - y2), y5 = y1 - (x1 - x2);
int x6 = x2 + (y1 - y2), y6 = y2 - (x1 - x2);
if (coordinatesSet.count(to_string(x5) + " " + to_string(y5)) > 0 &&
coordinatesSet.count(to_string(x6) + " " + to_string(y6)) > 0) {
squares.emplace_back(vector<vector<int>>{{x1, y1},
{x2, y2},
{x5, y5},
{x6, y6}});
}
}
}
cout << squares.size() / 4 << endl;
for (vector<vector<int>> &square: squares) {
// 正方形内部坐标排序
sort(square.begin(), square.end(), [](vector<int> &a, vector<int> &b) {
int x1 = a[0], y1 = a[1];
int x2 = b[0], y2 = b[1];
return x1 != x2 ? x1 < x2 : y1 < y2;
});
}
// 正方形之间排序
sort(squares.begin(), squares.end(), [](vector<vector<int>> &a, vector<vector<int>> &b) {
for (int i = 0; i < 4; i++) {
int x1 = a[i][0], y1 = a[i][1];
int x2 = b[i][0], y2 = b[i][1];
if (x1 != x2) {
return x1 < x2;
} else if (y1 != y2) {
return y1 < y2;
}
}
return false;
});
// 去重打印
string pre;
for (const auto &square: squares) {
string cur = "[[" + to_string(square[0][0]) + ", " + to_string(square[0][1]) + "]";
for (int i = 1; i < 4; i++) {
string tmp = ", [" + to_string(square[i][0]) + ", " + to_string(square[i][1]) + "]";
cur += tmp;
}
cur += "]";
if (cur != pre) {
cout << cur << endl;
pre = cur;
}
}
return 0;
}