POJ - 2002(静态哈希链表找正方形)

博客详细介绍了如何使用静态哈希链表解决POJ 2002问题,强调了正方形两点整数坐标推导其余两点的方法,以及避免重复计数的处理。文章提到了哈希表的实现细节,包括静态链表的创建和优化,并提醒了memset初始化的重要性以及输入处理的陷阱。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

POJ - 2002(静态哈希链表找正方形)

第一次做OJ,卡了好久, 稍微总结下.

原理

  • 注意如果正方形有两个点是整数, 余下的两个点一定是整数. 计算公式如下:

  • 已知: (x1,y1),(x2,y2) 则:

    x3=x1+(y1y2)y3=y1(x1x2)

    x4=x2+(y1y2)y4=y2(x1x2)

    x3=x1(y1y2)y3=y1+(x1x2)

    x4=x2(y1y2)y4=y2+(x1x2)

  • 注意:

    • 此题不需要去用float或者double类型.
    • 这种做法会使同一个正方形按照不同的顺序被枚举了四次,因此最后的结果要除以4.
    • 如果只取上面一种情况,就对半分,除以2, 其实是错的:
      如果我们把题意第一组测试实例4个点的顺序改变一下再输入,先不要除,会发现得到的的正方形的个数是不固定的,这就是由于输入的顺序照成的. 只能按照除4来计算.
  • 基本思想:枚举每一条边(两个点),然后根据相应的公式求出另外两个点,利用哈希进行查找这两个点是否存在即可

实现细节(其实都是踩过的坑 T_T)

  • 哈希表使用静态链表,这样不再需要开另外的链表, 直接用star数组的数据即可,有效的节省了空间. 使用的时候采取头插入.

    • 哈希数组工作原理 : 下标为哈希值 h , 存储的是哈希值为h的点的索引, 如果有多个哈希值为 h 的点, 则存储最后插入点的索引(因为使用头插入)

    • 静态链表的使用:

    • 创建

      开两个数组, 第一个数组hash[]存放头结点, 第二个数组next[]存放下一个结点的索引. 索引是和数据数组star[]绑定的.

      原本逻辑上的数据结构(注意这里的next指向的是相同hash值的下一个点,没有相同hash值时默认指向第0个元素, 也说明赋值和使用都不能用第0个元素, 静态链表千万不能用0!!!!!!):

      struct point
      {
      int x;
      int y;
      int next; 
      };
      point star[1005];

      因为我们要反复利用哈希静态链表, next值会频繁取出, 因此最为高效的方式应该是将next分离:

      struct point
      {
      int x;
      int y;
      };
      point star[1005];
      int next[1005];
  • 哈希表初始化用memset函数(在cstring类中), 可以减少操作时间. 如果让系统自动初始化会超时….

    即 memset初始化耗时 < 自动初始化耗时

    且全局变量在两种初始化同时存在时优先考虑memset函数.

  • memset函数使用注意事项:

    • 只能初始化为0,或者-1(即000000…..或者fffffffff…….)
  • 优先使用scanf,printf, 尽量不用cin, cout.

  • 注意题中如果给出了多组数据在一个实例中, 说明要对输入进行循环处理……(好坑啊!)

  • 关于~scanf(...)

调用scanf这个函数的返回值是成功读取的数据个数。在你这段程序里,如果正确输入了,也就是成功读取了一个数据,scanf的返回值为1;如果没有正确输入,那成功读取的数据个数就是0,也就是scanf的返回值为0.~的意思是把数据的各个二进制位反转。在C语言里不等于0就表示真。如果scanf返回值为0,那~scanf(…)就是~0,是一个二进制位全部为1的数,在C里当然表示真;如果scanf返回值为1,那~scanf(…)就是~1,是一个只有最后一个二进制位为0其余二进制位全部为1的数,在C里同样表示真。不论输入正确与否都为真,循环都会继续,因此~scanf(…)不能用于表示没有正确输入。只有在遇到文件结束或者输入(Ctrl+D或者Ctrl+Z)时,scanf返回EOF,是个所有二进制位都为1的数,这时~scanf(…)就是~EOF,是一个所有二进制位都为0的数,也就是0,表示假,这时候循环才会结束。所以,~scanf(…)是表示没有遇到文件结束或者输入(Ctrl+D或者Ctrl+Z),当然这时候自然也不会有正确输入。来自 https://zhidao.baidu.com/question/464277961.html

代码:

#include <iostream>
#include <cstdio>
#include <cstring>

#define MOD 20007

using namespace std;

struct point
{
    int x;
    int y;
};

point star[1005];
int next[1005];

int hash[MOD+5];


void insert(point a,int index)
{
    int h = (a.x*a.x+a.y*a.y)%MOD;
    next[index] = hash[h];//***************注意使用
    hash[h] = index;
}

bool find(point a)
{
    int h = (a.x*a.x + a.y*a.y) %MOD;
    int p =  hash[h];
    while(p)
    {
        if(star[p].x==a.x && star[p].y==a.y)
            return true;
        p = next[p];//********************注意使用
    }
    return false;
}


int AIn(point a1, point a2)
{
    int ans = 0;

    point b1,b2;

    int dx = (a1.x-a2.x);
    int dy = (a1.y-a2.y);

    b1.x = a1.x + dy;
    b1.y = a1.y - dx;
    b2.x = a2.x + dy;
    b2.y = a2.y - dx;
    if(find(b1) && find(b2))
        ans++;

    b1.x = a1.x - dy;
    b1.y = a1.y + dx;
    b2.x = a2.x - dy;
    b2.y = a2.y + dx;   
    if(find(b1) && find(b2))
        ans++;

    return ans;
}


int main()
{
    int n;
    while( ~scanf("%d",&n) && n)
    {   
        memset(hash,0,sizeof(hash));
        memset(next,0,sizeof(next));

        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d",&star[i].x,&star[i].y);
            insert(star[i],i);
        }   

        int cnt = 0;
        for (int i = 1; i <= n; i++)
            for (int j = i+1; j <= n; j++)
                cnt += AIn( star[i],star[j]);

        printf("%d\n",cnt >> 2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值