AcWing 137. 雪花雪花雪花(数组模拟哈希表)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

N 片雪花,每片雪花 由六个角组成每个角都有长度

i 片雪花 六个角的长度 从某个角开始顺时针依次记为 ai,1、ai,2、…、 ai,6

因为雪花的形状是封闭的环形,所以 从任何一个角开始顺时针或逆时针往后记录长度,得到的六元组都代表形状相同的雪花。

例如:ai,1、ai,2、…、ai,6ai,2、ai,3、…、ai,6、ai,1 就是形状相同的雪花ai,1、ai,2、…、ai,6ai,6、ai,5、…、ai,1 也是形状相同的雪花

我们定义 两片雪花形状相同,当且仅当它们 各自从某一个角开始顺时针或逆时针记录长度,能得到两个相同的六元组

N 片雪花中 是否存在 两片形状相同 的雪花。

思路:

观察数据范围,我们应该将复杂度控制在 O(n)O(nlogn) 的范围内,这里我们使用 O(n) 的哈希表做法

先定义 Hash 函数
在这里插入图片描述

(其中 Р 是我们自己选取的一个较大的质数

显然,对于两片形状相同的雪花,它们六个角的长度之和、长度之积都相等,因此它们的 Hash 函数值也相等,存储到 哈希表同一个表头下的链表 中(即 一个 Hash 值对应 一条链表)。

建立一个 HashN 片雪花依次插入

对于 每片雪花 ai,1、ai,2、ai,6,先用上方定义的 哈希函数 求出其对应的 哈希值 haval,之后直接 O(1) 的复杂度 找到 表头 h(haval) 对应的链表,并 O(常数) 的复杂度 扫描该链表检查是否存在与 ai,1、ai,2、…、ai,6 形状相同的雪花 ,如果存在则表明 有两片相同的雪花,反之则 将新的雪花存入当前扫描的链表。

如果 N 次插入的雪花之前都没有往哈希表中存入相同的,则判断为 不存在两片相同的雪花。

检查两片雪花是否形状相同 时,由于 每片雪花角的个数仅仅只有 6,因此这里我们可以直接 暴力模拟判断。之后我们会学习 循环同构串 的 “最小表示法”,这个方法可以进一步提高判断两片雪花形状是否相同的 效率

时间复杂度:

对于 随机数据期望的时间复杂度为 O(N ^ 2 / P)P 取为 最接近 N 的质数期望的时间复杂度为 O(N)

代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
#define int long long
typedef vector<int> vi;
#define pb push_back
#define pp pop_back
const int N = 1e5 + 10, P = 99991, M = 10;
int n;
int a[M];
int h[N], e[N][M], ne[N], idx;

int H(int a[])	//哈希函数
{
    int sum = 0, pw = 1;
    for (int i = 0; i < 6; ++i)
    {
        sum = (sum + a[i]) % P;
        pw = pw * a[i] % P;
    }
    return (sum + pw) % P;
}

bool isame(int a[], int b[])	//暴力模拟判断,注意根据题意,正反都要做一次
{
    vi aa, bb;
    for (int i = 0; i < 6; ++i) aa.push_back(a[i]);

    for (int i = 0; i < 6; ++i)
    {
        bb.clear();
        for (int j = i; j < 6; ++j) bb.push_back(b[j]);
        if (bb.size() < 6)
        {
            int tmp = 6 - bb.size();
            for (int i = 0; i < tmp; ++i) bb.push_back(b[i]);
        }
        if (aa == bb) return true;
    }

    aa.clear(), bb.clear();
    reverse(a, a + 6);

    for (int i = 0; i < 6; ++i) aa.push_back(a[i]);

    for (int i = 0; i < 6; ++i)
    {
        bb.clear();
        for (int j = i; j < 6; ++j) bb.push_back(b[j]);
        if (bb.size() < 6)
        {
            int tmp = 6 - bb.size();
            for (int i = 0; i < tmp; ++i) bb.push_back(b[i]);
        }
        if (aa == bb) return true;
    }

    return false;
}

bool find(int a[])	//查找哈希表中当前雪花对应表头的链表,看看是否之前出现过
{
    int haval = H(a);
    for (int i = h[haval]; ~i; i = ne[i])
    {
        if (isame(e[i], a)) return true;	//出现过,直接返回为真
    }
    //未出现过,拉链法插入哈希表。并返回为假(类比之前链式前向星建图方式即可)
    memcpy(e[idx], a, 6 * sizeof(int)), ne[idx] = h[haval], h[haval] = idx++;
    return false;	
}

signed main()
{
    int T = 1; //cin >> T;

    while (T--)
    {
        int n; cin >> n;
        memset(h, -1, sizeof h);
        int t = n;
        while (t--)
        {
            for (int i = 0; i < 6; ++i) cin >> a[i];
            if (find(a))
            {
                puts("Twin snowflakes found.");
                return 0;
            }
        }
        puts("No two snowflakes are alike.");

    }

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值