题意描述:有一堆平面散点集,任取四个点,求能组成正方形的不同组合方式有多少。相同的四个点,不同顺序构成的正方形视为同一正方形。
思路变迁:
1、最简单的方法,直接暴力搜索,即依次取四个顶点,根据其坐标判断是否能组成正方形。组成正方形的条件是四个顶点可组成的六条边里面,有四条相等,剩下两条相等。当然由于其时间复杂度为O(n^4),所以提交结果为TLE
2、考虑降低时间复杂度。如任取三个顶点,根据组成正方形的条件计算得到第四个顶点,判断其是否在点的集合内,其复杂度为O(n^3)。或者任取两个顶点,根据组成正方形的条件计算出另外两个顶点,再判断其是否在点的集合内,其复杂度为O(n^2)。
3、不论是任取两个顶点还是任取三个顶点,其关键一步都是判断计算得到的新的点,是否在点的集合内,基本方法有对点排序再二分查找,或者是使用hash表。我的设计是考虑点所处象限的二维hash表。根据这道题的提交结果来看,所用时间上,堆排序优于快排
4、对于任选两个顶点时,可能存在三种不同的正方形。但是在对所有顶点排序后,如若按序来去顶点,则可发现我们只需始终计算一个方向的即可,且计算结果中同一个正方形重复了两次,所以最终结果除以二。如若不排序直接任取两个的顶点的话,始终按一个方向计算,可以发现每个正方形的四条边,每一条边都会贡献一次得到的正方形的个数,所以最终结果需要除以四
5、在使用hash表后时间复杂度达到最优,即O(nlgn) + hash表平均冲突*n(n-1)/2,所以本问题的进一步优化,取决于怎么更大程度的降低hash表的冲突?考虑位运算,来存储所有的点,但需要空间太大,因为共有40000*40000个点
暴力搜索代码
1 #include<stdio.h> 2 #define SIZE 1005 3 typedef struct POINT { 4 int x; 5 int y; 6 } point; 7 int isSquare(int a, int b, int c, int d); 8 point p[SIZE]; 9 int main() { 10 int n, result; 11 int i, j, k, x; 12 scanf("%d", &n); 13 while(n != 0) { 14 result = 0; 15 for(i=1; i<=n; i++) 16 scanf("%d %d", &p[i].x, &p[i].y); 17 for(i=1; i<=n; i++) { 18 for(j=i+1; j<=n; j++) { 19 for(k=j+1; k<=n; k++) { 20 for(x=k+1; x<=n; x++) { 21 if(isSquare(i, j, k, x)) 22 result++; 23 } 24 } 25 } 26 } 27 printf("%d ", result); 28 scanf("%d", &n); 29 } 30 } 31 32 int isSquare(int a, int b, int c, int d) { 33 int s[6]; 34 int num1 = 1, num2 = 1; 35 int side1 = -1, side2 = -1; 36 int i; 37 s[0] = (p[a].x - p[b].x)*(p[a].x - p[b].x) + (p[a].y - p[b].y)*(p[a].y - p[b].y); 38 s[1] = (p[a].x - p[c].x)*(p[a].x - p[c].x) + (p[a].y - p[c].y)*(p[a].y - p[c].y); 39 s[2] = (p[a].x - p[d].x)*(p[a].x - p[d].x) + (p[a].y - p[d].y)*(p[a].y - p[d].y); 40 s[3] = (p[b].x - p[c].x)*(p[b].x - p[c].x) + (p[b].y - p[c].y)*(p[b].y - p[c].y); 41 s[4] = (p[b].x - p[d].x)*(p[b].x - p[d].x) + (p[b].y - p[d].y)*(p[b].y - p[d].y); 42 s[5] = (p[c].x - p[d].x)*(p[c].x - p[d].x) + (p[c].y - p[d].y)*(p[c].y - p[d].y); 43 for(i=0; i<=5; i++) { 44 if(i == 0) { 45 side1 = s[i]; 46 } 47 else if(s[i] != side1) { 48 if(side2 == -1) 49 side2 = s[i]; 50 else if(s[i] == side2) 51 num2++; 52 else 53 break; 54 } 55 else if(s[i] == side1) 56 num1++; 57 } 58 if((num1==2 && num2==4) || (num1==4 && num2 == 2)) 59 return 1; 60 else 61 return 0; 62 }
使用堆排序+二分查找代码
1 #include<stdio.h> 2 #define SIZE 1005 3 typedef struct POiNT { 4 int x; 5 int y; 6 } point; 7 int cmp(point pa, point pb); 8 void heapSort(int n); 9 int binarySearch(int low, int high, point key); 10 void shiftDown(int i, int n); 11 void swap(int i, int j); 12 point p[SIZE]; 13 int main() { 14 int n, result; 15 int i, j, k, x; 16 point pa, pb; 17 scanf("%d", &n); 18 while(n != 0) { 19 result = 0; 20 for(i=1; i<=n; i++) 21 scanf("%d %d", &p[i].x, &p[i].y); 22 heapSort(n); 23 for(i=1; i<=n; i++) 24 printf("%d %d ", p[i].x, p[i].y); 25 26 for(i=1; i<=n; i++) { 27 for(j=i+1; j<=n; j++) { 28 pa.x = p[i].x - (p[j].y - p[i].y); 29 pa.y = p[i].y + (p[j].x - p[i].x); 30 pb.x = pa.x + (p[j].x - p[i].x); 31 pb.y = pa.y + (p[j].y - p[i].y); 32 if(binarySearch(1, n, pa) && binarySearch(1, n, pb)) 33 result++; 34 } 35 } 36 printf("%d ", result/2); 37 scanf("%d", &n); 38 } 39 } 40 int binarySearch(int low, int high, point key) { 41 int mid = (low + high)/2; 42 if(low > high) 43 return 0; 44 if(cmp(p[mid], key) == 0) 45 return 1; 46 else if(cmp(p[mid], key) == 1) 47 return binarySearch(low, mid-1, key); 48 else 49 return binarySearch(mid+1, high, key); 50 } 51 void heapSort(int n) { 52 int i = n/2; 53 for(; i>0; --i) 54 shiftDown(i, n); 55 for(i=n; i>1;) { 56 swap(1, i); 57 shiftDown(1, --i); 58 } 59 } 60 void shiftDown(int i, int n) { 61 int j = 2*i; 62 for(; j<=n; j=2*i) { 63 if(j < n && cmp(p[j], p[j+1]) == -1) 64 j = j + 1; 65 if(cmp(p[i], p[j]) == -1) { 66 swap(i, j); 67 i = j; 68 } else 69 break; 70 } 71 } 72 void swap(int i, int j) { 73 point temp = p[i]; 74 p[i] = p[j]; 75 p[j] = temp; 76 } 77 int cmp(point pa, point pb) { 78 if(pa.x > pb.x) 79 return 1; 80 else if(pa.x == pb.x && pa.y > pb.y) 81 return 1; 82 else if(pa.x == pb.x && pa.y == pb.y) 83 return 0; 84 else 85 return -1; 86 }
考虑象限的hash代码
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #define SIZE 1013 5 typedef struct POiNT { 6 int x; 7 int y; 8 } point; 9 struct hashNode { 10 point poi; 11 struct hashNode *next; 12 }; 13 int cmp(const void *a, const void *b ); 14 void add(point key); 15 int find(point); 16 point p[SIZE]; 17 struct hashNode hashTable[5][SIZE]; 18 int main() { 19 int n, result; 20 int i, j, k, x; 21 point pa, pb; 22 scanf("%d", &n); 23 while(n != 0) { 24 result = 0; 25 memset(hashTable, 0, sizeof(hashTable)); 26 for(i=1; i<=n; i++) { 27 scanf("%d %d", &p[i].x, &p[i].y); 28 add(p[i]); 29 } 30 qsort(p+1, n, sizeof(p[0]), cmp); 31 for(i=1; i<=n; i++) { 32 for(j=i+1; j<=n; j++) { 33 pa.x = p[i].x - (p[j].y - p[i].y); 34 pa.y = p[i].y + (p[j].x - p[i].x); 35 pb.x = pa.x + (p[j].x - p[i].x); 36 pb.y = pa.y + (p[j].y - p[i].y); 37 if(find(pa) && find(pb)) 38 result++; 39 } 40 } 41 printf("%d ", result/2); 42 scanf("%d", &n); 43 } 44 } 45 void add(point key) { 46 int t, num; 47 struct hashNode *newNode; 48 if(key.x >= 0 && key.y >= 0) 49 t = 1; 50 else if(key.x >= 0 && key.y < 0) 51 t = 2; 52 else if(key.x < 0 && key.y < 0) 53 t = 3; 54 else 55 t = 4; 56 num = (key.x*key.x + key.y*key.y)%SIZE; 57 newNode = (struct hashNode*)malloc(sizeof(struct hashNode)); 58 newNode->poi = key; 59 newNode->next = hashTable[t][num].next; 60 hashTable[t][num].next = newNode; 61 } 62 int find(point key) { 63 int t, num; 64 struct hashNode *node; 65 if(key.x >= 0 && key.y >= 0) 66 t = 1; 67 else if(key.x >= 0 && key.y < 0) 68 t = 2; 69 else if(key.x < 0 && key.y < 0) 70 t = 3; 71 else 72 t = 4; 73 num = (key.x*key.x + key.y*key.y)%SIZE; 74 node = hashTable[t][num].next; 75 while(node) { 76 if(node->poi.x == key.x && node->poi.y == key.y) 77 return 1; 78 node = node->next; 79 } 80 return 0; 81 } 82 int cmp(const void *a, const void *b ) 83 { 84 point c = *(point*)a; 85 point d = *(point*)b; 86 87 if(c.x == d.x) 88 return c.y - d.y; 89 else 90 return c.x - d.x; 91 }