[POJ 3349] Snowflake Snow Snowflakes

- 大致题意
在n (n<100000)个雪花中判断是否存在两片完全相同的雪花,每片雪花有6个角,每 个角的长度限制为1000000
两片雪花相等的条件:
雪花6个角的长度按顺序相等(这个顺序即可以是顺时针的也可以是逆时针的)
- 解题思路
Hash,连加求余法 求key 值,链地址法解决冲突

设雪花6片叶子的长度为len1~len6

key=( len1+len2+len3+len4+len5+len6)%prime

=( len1%prime +len2%prime +len3%prime +len4%prime +len5%prime +len6)%prime

为了避免重复,这里的prime要足够大,可以取prime=99991

- 基本做法

从上面的处理手法能够知道:

当且仅当两片雪花的key值一样时,这两片雪花才有可能相同。

在输入第k个雪花信息的时候,先求出其key值,若在hash[]中key值没有出现过,则直接存放信息。但如果在hash[key]中已经保存有其他地址,说明此前出现过key值相同的其他雪花,这些雪花信息以链表的形式存放在hash[key]中,这时在为第k个雪花信息寻找存放空间的同时,必然在链表中逐一访问这些具有相同key值得雪花,所以我们就能在寻址的同时,顺便逐一比较第k个雪花与这些雪花的信息,一旦发现k与某个雪花是一样的,则标记,然后等待后续输入完成后,直接输出寻找到两片一样的雪花。

但是当所有输入结束后都没有标记过,则说明不存在一样的雪花。

这时肯定又会有同学有疑问:
Key值只能说明两片雪花的叶子长度之和相等,但是不能说明6片叶子分别相等,更加不能说明6片叶子按顺序相等。那么当我们寻找到key值相同的两片雪花时,我们该如何比较两片雪花?

其实是可以的。假设有两片雪花,A 和B

我们固定A,先按顺时针方向比较:
若A0B0,则按顺序比较A1和B1…比较A5和B5
只要当出现Ai != Bi,则把B顺时针转动一次,
若A0
B1,则按顺序比较A1和B2…比较A5和B0
。。。。。
以此类推,直至B转动了5次,若还不相同,则说明这两片雪花在顺时针方向不等。

再比较逆时针方向:

同样固定A,若A0B5,则按顺序比较A1和B4…比较A5和B0
只要当出现Ai != B(5-i),则把B逆时针转动一次,
若A0
B4,则按顺序比较A1和B3…比较A5和B5
以此类推,直至B转动了5次,若还不相同,则说明这两片雪花在逆时针方向不等。

如是者,比较两片雪花最坏的情况为要比较362 = 72 次!!!
可想而知当n=10W时,若任意两片比较,则最坏要比较 72
(10W)^2次

在这里给出两条公式:

设i为A、B的第i片叶子,j为B当前顺时针转过的格数

那么 A(i) —> B( (i+j)%6 )

设i为A、B的第i片叶子,j为B当前逆时针转过的格数

那么 A(i) —> B( (5-i-j+6)%6 )

因此,为了尽可能第降低比较次数,那么我们就需要把雪花按key值分类,此时就务求prime在恰当的范围内尽可能大,使得地址冲突 (出现两个或以上key值相同的雪花) 的情况尽可能降到最低,最理想的情况就是:当且仅当两片雪花是相同的时候,他们的key值才相等。那么根据前面的算法思路(只对key值相同的两片雪花进行比较),在最理想情况下,我们最多仅需比较1次就能得到“存在雪花相同”结果,最少比较0次就能得到“不存在一样的雪花”的结果。

  • 代码::
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=100010;
const int P=99991;

int n;
int a[10];//输入的雪花数据
int snow[maxn][10]; //保存hash值确定的雪花
int len=0,head[maxn],next[maxn];//模拟链表,head 表头,next,下一个到达的位置

int hash()//获得hash值,通过加法和乘法来确定hash值
{
    long long sum=0,mul=1;
    for(int i=1;i<=6;i++)
    {
        sum=(sum+a[i])%P;
        mul=(mul*a[i])%P;
    }
    return (sum+mul)%P;
}

bool equal(int *b)
{
    for(int i=1;i<=6;i++)
        if(a[i]!=b[i]) return false;
    return true;
}

void zxbsf()  //最小表示法可以参考另一篇博文:
//https://blog.csdn.net/flymoyu/article/details/88365771
{//通过这种方式来确定字典树的位置,便于后期的比较
    int i,j,k;
    for(i=1;i<=6;i++)
        a[i+6]=a[i];
    for(i=1,j=2;i<=6&&j<=6;)
    {
        for(k=0;k<6&&a[i+k]==a[k+j];k++)
          if(k==6) break;
        if(a[i+k]<a[j+k])
        {
            j=j+k+1;
            if(i==j) j++;
        }
        else
        {
            i=i+k+1;
            if(i==j) i++;
        }


    }
    int g1=i>6?j:i;
    for(i=12,j=11;i>6&&j>6;)
    {
        for( k=0;k<6&&a[i-k]==a[j-k];k++)
               if(k==6) break;
            if(a[i-k]<a[j-k])
            {
                j=j-k-1;
                if(i==j) j--;
            }
            else
            {
                i=i-k-1;
                if(i==j) i--;
            }

    }
    int g2=i<=6?j:i;
    for(i=0;i<6;i++)
    {
        if(a[g1+i]<a[g2-i])
        {
            g2=-1;break;
        }
        if(a[g1+i]>a[g2-i])
        {
            g1=-1;break;
        }
    }
    if(g2==-1)
        for(i=1;i<=6;i++) a[i]=a[g1+i-1];
    else
    {
        int tmp[10];
        for(i=1;i<=6;i++) tmp[i]=a[g2-i+1];
        for( i=1;i<=6;i++) a[i]=tmp[i];
    }

}

bool have()  
{
    zxbsf();
    int h=hash();
    for(int i=head[h];i;i=next[i]) //若存在这样的hash值,就往下操作
    {
        if(equal(snow[i])) return true;
    }
    //模拟链表
    len++;
    next[len]=head[h];
    head[h]=len;
    for(int i=1;i<=6;i++)
        snow[len][i]=a[i];
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=6;j++) cin>>a[j];
        if(have())
        {
            printf("%s","Twin snowflakes found.");
            return 0;
        }

    }
    printf("%s","No two snowflakes are alike.");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值