哈希表:正方形

泄露秘密 提交于 2019-12-10 20:01:47

摘一下题目:

正方形

总时间限制: 3500ms 内存限制: 65536kB
描述
给定直角坐标系中的若干整点,请寻找可以由这些点组成的正方形,并统计它们的个数。
输入
包括多组数据,每组数据的第一行是整点的个数n(1<=n<=1000),其后n行每行由两个整数组成,表示一个点的x、y坐标。输入保证一组数据中不会出现相同的点,且坐标的绝对值小于等于20000。输入以一组n=0的数据结尾。
输出
对于每组输入数据,输出一个数,表示这组数据中的点可以组成的正方形的数量。
样例输入
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
样例输出
1
6
1
本来是个大水题,但是首先我把初高中数学知识全给忘了,其次数据量非常大,必须自己手写哈希表。
我最开始的思路是以两个点的中点作为哈希值把这些所有点放进各个桶里,再在每个桶的所有点里面检测。但是这样TLE了……因为n^2级别的数据放进n级别的桶里……再两两比较……代价……emmm
参考了别人的思路,是两两取出点,再计算另外两个点坐标,看是否有这两个点。
我看到的一个哈希表的代码是
最开始的辣鸡代码:

#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 1005;
struct point
{
    double x, y;
    point() {}
    point(double _x, double _y) : x(_x), y(_y) {}
} pts[maxn];
struct points
{
    point p1, p2;
    points() {}
    points(point &_p1, point &_p2) : p1(_p1), p2(_p2) {}
};
double dot(points& pp1, points& pp2)
{
    return ((pp1.p1.x - pp1.p2.x) * (pp2.p1.x - pp2.p2.x) + (pp1.p1.y - pp1.p2.y) * (pp2.p1.y - pp2.p2.y));
}
double dis(points& p)
{
    return sqrt((p.p1.x - p.p2.x) * (p.p1.x - p.p2.x) + (p.p1.y - p.p2.y) * (p.p1.y - p.p2.y));
}
bool issquare(points& pp1, points& pp2)
{
    if ((pp1.p1.x + pp1.p2.x) != (pp2.p1.x + pp2.p2.x) || ((pp1.p1.y + pp1.p2.y) != (pp2.p1.y + pp2.p2.y)))
        return 0;
    if (dot(pp1, pp2) != 0)
        return 0;
    if (dis(pp1) != dis(pp2)) return 0;
    return 1;
}
int hashtable(point a)
{
    return (long)((a.x * a.x + a.y * a.y)) % 1009;
}
vector<points> sqr[1020];
int main()
{
    int n;
    while (cin >> n && n)
    {
        double x, y;
        memset(sqr, 0, sizeof(sqr));
        memset(pts, 0, sizeof(pts));
        for (int i = 0; i < n; i++)
        {
            cin >> x >> y;
            pts[i] = point(x, y);
        }
        for (int i = 0; i < n-1; i++)
        {
            for (int j = i + 1; j < n; j++)
            {
                cout << hashtable(point((pts[i].x + pts[j].x), (pts[i].y + pts[j].y))) << endl;
                sqr[hashtable(point((pts[i].x + pts[j].x), (pts[i].y + pts[j].y)))].push_back(points(pts[i], pts[j]));
            }
        }
        int cnt = 0;
        for (int iter = 0; iter < 1009; ++iter)
        {
            cout << sqr[iter].size() << " ";
            for (int i = 0; i < sqr[iter].size(); i++)
            {
                for (int j = i + 1; j < sqr[iter].size(); j++)
                {
                    //cout << iter->second[i] << iter->second[j] << endl;
                    if (issquare(sqr[iter][i], sqr[iter][j]))
                        ++cnt;
                }
            }
        }
        cout << cnt << endl;
    }
    //system("pause");
    return 0;
}

当然是TLE的……之前那几个连洛谷都过不了的要么就是正方形判定条件错了,要么就是点乘写错了……我是怎么混过高考的啊……
看到的网上答案是开哈希,也就是每个哈希值一个链表。代码如下(侵删,好像只有一篇CSDN博客是这个的,但是找不到链接了,代码是对拍的时候抄下来的)

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct po{
	int x,y;
}a[1001];
int heade[2001],nexte[2001],px[2001],py[2001];
int t,ans,cnt;
void chushihua()
{
	ans=0,cnt=0;
	memset(heade,0,sizeof(heade));
}
void inhash(int x,int y)
{
	int k=(x*x+y*y)%2001;
	px[++cnt]=x;py[cnt]=y;
	nexte[cnt]=heade[k];
	heade[k]=cnt;
}
int fihash(int x,int y)
{
	int k=(x*x+y*y)%2001;
	int n=heade[k];
	while(n!=0)
	{
		if(px[n]==x&&py[n]==y) return 1;
		else n=nexte[n];
	}
	return 0;
}
int main()
{
	cin>>t;
	while(t!=0)
	{
		chushihua();
		for(int i=1;i<=t;++i)
		{
			cin>>a[i].x>>a[i].y;
			inhash(a[i].x,a[i].y);
		}
		for(int i=1;i<=t;++i)
		 for(int j=i+1;j<=t;++j)
		 {
		 	int x1=a[i].x-(a[i].y-a[j].y);
		 	int y1=a[i].y+(a[i].x-a[j].x);
		 	int x2=a[j].x-(a[i].y-a[j].y);
		 	int y2=a[j].y+(a[i].x-a[j].x);
		 	if(fihash(x1,y1)&&fihash(x2,y2))
		 	ans++;
		 }
		for(int i=1;i<=t;++i)
		 for(int j=i+1;j<=t;++j)
		 {
		 	int x1=a[i].x+(a[i].y-a[j].y);
		 	int y1=a[i].y-(a[i].x-a[j].x);
		 	int x2=a[j].x+(a[i].y-a[j].y);
		 	int y2=a[j].y-(a[i].x-a[j].x);
		 	if(fihash(x1,y1)&&fihash(x2,y2))
		 	ans++;
		 }
		 ans >>= 2;
		 cout<<ans<<endl;
		 cin>>t;
	}
	return 0;
} 

下面是AC的代码:

#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1005;
const long long primenumber = 10007;
int px[maxn];
int py[maxn];
int hashtable[primenumber+5];
int hashfunction(long long x, long long y) {
    //return (long long)((x+20000)*1009ll*1009ll + (y+20000)*1009ll) % primenumber;
    return (long long)(x*x+y*y)%primenumber;
}
int func(int j, int x, int y) {
    long long i = 1;
    long long h = hashfunction(x, y);
    for (; h != j; h = (h+i*i)%10007ll, i++);
    return (h+i*i)%10007ll;
}
void inhash(int idx) {
    long long hashnumber = hashfunction(px[idx], py[idx]);
    if (hashtable[hashnumber] == -1) {
        hashtable[hashnumber] = idx;
    }
    else {
        int i;
        for (i = hashnumber; hashtable[i] != -1; i = func(i, px[idx], py[idx]));
        hashtable[i] = idx;
    }
}
bool findhash(int x, int y)
{
    int hashnumber = hashfunction(x, y);
    int idx = hashtable[hashnumber];
    int temp = hashnumber;
    while (idx != -1) {
        if (px[idx] == x && py[idx] == y) return 1;
        temp = func(temp, x, y);
        idx = hashtable[temp];
    }
    return 0;
}
int main()
{
    int n;
    while (cin >> n && n)
    {
        memset(px, 0, sizeof(px));
        memset(py, 0, sizeof(py));
        memset(hashtable, -1, sizeof(hashtable));
        for (int i = 0; i < n; i++)
        {
            cin >> px[i] >> py[i];
            inhash(i);
        }
        int cnt = 0;
        for (int i = 0; i < n; i++)
        {
            for (int j = i + 1; j < n; j++)
            {
                double x1 = (px[i] + px[j] + py[j] - py[i]) / 2.0;
                double x2 = (px[i] + px[j] - py[j] + py[i]) / 2.0;
                double y2 = (py[i] + py[j] + px[j] - px[i]) / 2.0;
                double y1 = (py[i] + py[j] - px[j] + px[i]) / 2.0;
                if (double((int)x1) != x1 || double((int)x2) != x2 || double((int)y1) != y1 || double((int)y2) != y2) continue;
                if (findhash(x1, y1) && findhash(x2, y2))
                    ++cnt;
            }
        }
        cout << cnt/2 << endl;
    }
    return 0;
}

参考了这个博主的代码,函数名都有重复的。而且哈希函数也是这位博主的。我以前的哈希函数是多项式函数,我没有试那个能不能过,但是对拍出了问题,就改成了这个。
搞得我把所有东西都改成了long long,但其实写挂的地方是我在findhash那里一开始传了idx,结果后面命名了个一样名字的变量给魔改了……后面我以为是那个循环有歧义,决定重写一遍的时候发现的……
(卖脑壳lab好像也是我以为循环有歧义决定重写的时候发现写挂了……)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!