POJ #2002 Squares 枚举+哈希 数学(正方形与三角形的关系) 静态链接法哈希

Description


A square is a 4-sided polygon whose sides have equal length and adjacent sides form 90-degree angles. It is also a polygon such that rotating about its centre by 90 degrees gives the same polygon. It is not the only polygon with the latter property, however, as a regular octagon also has this property. 


So we all know what a square looks like, but can we find all possible squares that can be formed from a set of stars in a night sky? To make the problem easier, we will assume that the night sky is a 2-dimensional plane, and each star is specified by its x and y coordinates. 

Input

The input consists of a number of test cases. Each test case starts with the integer n (1 <= n <= 1000) indicating the number of points to follow. Each of the next n lines specify the x and y coordinates (two integers) of each point. You may assume that the points are distinct and the magnitudes of the coordinates are less than 20000. The input is terminated when n = 0.

Output

For each test case, print on a line the number of squares one can form from the given stars.

Sample Input

4
1 0
0 1
1 1
0 0
9
0 0
1 0
2 0
0 2
1 2
2 2
0 1
1 1
2 1
4
-2 5
3 7
0 0
5 2
0

Sample Output

1
6
1

思路


  题目很简单,找到由点组成的正方形的最大数量。

  为了方便穷举,先对点的x坐标从小到大、y坐标从小到大进行排序(x优先排序)。我在这道题在枚举一条边的时候卡了不少时间,首先是数学知识,由正方形两点的坐标可确定另外两点的坐标,原理是一个正方形由四个等边三角形组成。那么,很容易就可以得到另外两个点的坐标:

  

          图例说明正方形四个点的坐标关系可用三角形的边表示

 

  其次,我们选择正方形的左上、左下两点穷举另外两点坐标,从而得到一个正方形。然而在穷举的过程中我只考虑了从左往右看的情况而忽视了从上向下看的情况,举个例子,选择上图 c 点、d 点一样能成功穷举正方形,此时 c-d 边是左边,而 a-b 边是右边,这和 a-c 边是左边、b-d 边是右边是一样的。所以得到的答案是正确答案的两倍,在最后输出时需要除2。

  最后,查找另外两点的时候利用哈希以节约时间,否则可能 TLE。

  根据白书的指导,试了试利用两数组静态地模拟拉链数组进行哈希,一个是 head 数组,用于存储 i 位置上的元素个数,一个是 next 数组,存储当前链表的上一个元素位于原集合 p[] 中的索引。

  白书中的构建方法如下:

const int hashsize = 1000003;
int head[hashsize], next[maxstate];

void init_lookup_table( ) { memset(head, 0, sizeof(head)); }

int hash(State& s){
    int v = 0;
    for(int i = 0; i < 9; i++) v = v * 10 + s[i]; //把9个数字组合成9位数return v % hashsize;
    //确保hash函数值是不超过hash表的大小的非负整数
}

int try_to_insert(int s){
    int h = hash(st[s]);
    int u = head[h];  //从表头开始查找链表
    while(u){
        if(memcmp(st[u],st[s], sizeof(st[s]))==0)return 0; //找到了,插入失败
        u = next[u];  //顺着链表继续找
    }
    next[s] = head[h]; //插入到链表中
    head[h] = s;
    return 1;
}

  做完这道题后发现自己在解计算机图形问题时很容易思维定势,因为计算机根本没有“左” or “右”的概念,以后要小心。

  

  AC代码如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 1010
int n;
struct Point{
    int x;
    int y;
}p[MAXN];

bool cmp (const Point& a, const Point& b) {
    if (a.x == b.x){
        return a.y < b.y;
    }
    return a.x < b.x;
}

//拉链法构造哈希表
int head[MAXN], next[MAXN];

void init_table() {
    memset(head, -1, sizeof(head));
    memset(next, -1, sizeof(next));
}

int hash(const Point& tmp) {
    return std::abs(tmp.x + tmp.y) % MAXN;
}

bool insert(int i) {
    int h = hash(p[i]);
    int u = head[h]; //从"表头"开始查找链表(实际是表尾)
    while(u != -1) {
        if (p[u].x == p[i].x && p[u].y == p[i].y) return false; //已找到,插入失败
        u = next[u]; //顺着链表继续查
    }

    next[i] = head[h]; //插入链表中
    head[h] = i;
    return true;
}

bool search(const Point& tmp) {
    int h = hash(tmp);
    int u = head[h];
    while(u != -1) {
        if (p[u].x == tmp.x && p[u].y == tmp.y ) return true;
        u = next[u];
    }
    return false;
}

int main(void) {
    while(scanf("%d", &n) && n) {
        init_table();
        for (int i = 0; i < n; i++)
            scanf("%d%d", &p[i].x, &p[i].y);
        std::sort(p, p+n, cmp); //x从小到大、y从小到大排序点,x优先
        for (int i = 0; i < n; i++)
            insert(i);

        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j < n; j++) {
                Point t1, t2;
                int dx = p[j].x - p[i].x;
                int dy = p[j].y - p[i].y;
                t1.x = p[i].x + dy;
                t1.y = p[i].y - dx;
                if (search(t1)) {
                    t2.x = p[j].x + dy;
                    t2.y = p[j].y - dx;
                    if(search(t2)){
                        ans++;
                        /*
                        std::cout << "构成的正方形的四个顶点坐标是:" << std::endl;
                        printf("(%d,%d), (%d,%d), (%d,%d), (%d,%d)
", p[i].x, p[i].y,
                                p[j].x, p[j].y, t1.x, t1.y, t2.x, t2.y);
                        std::cout <<"此时 i j 分别为" << i << " " << j << std::endl;
                        */
                    }
                    else continue;
                }
                else continue;
            }
        }
        printf("%d
", ans/2);
    }
    return 0;
}
View Code
————全心全意投入,拒绝画地为牢
原文地址:https://www.cnblogs.com/Bw98blogs/p/8540377.html