poj3349 Snowflake Snow Snowflakes(Hash)

题意

给你n朵雪花,每朵雪花有6片花瓣,每片花瓣有一个权值。花瓣的权值可以从任意位置的顺时针或逆时针来读,判断是否有两朵相同的雪花。

题解1

暴力hash
我们约定,一朵雪花的hash值为\small \sum a[i]+\prod a[i] \displaystyle mod P,P取质数99991。对于相同的hash,我们将其各边用邻接表记录,对其中进行暴力比对:枚举起点,顺着、逆着各判一次。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int P=99991;

int n;
int a[10];
int snow[maxn][10];
int len=0,head[maxn],next[maxn];

int 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++)//起点
    {
        bool flag=true;
        for(int ki=i,kj=1;kj<=6;kj++)//顺时针
        { 
            if(a[ki]!=b[kj]){flag=false;break;}
            ki++;
            if(ki==7) ki=1;
        }
        if(flag) return true;
        flag=true;
        for(int ki=i,kj=1;kj<=6;kj++)//逆时针
        {
            if(a[ki]!=b[kj]){flag=false;break;}
            ki--;
            if(ki==0) ki=6;
        }
        if(flag) return true;
    }
    return false;
}

bool have()
{
    //判断有无重复 
    int h=hash();
    for(int i=head[h];i;i=next[i])
    {
        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++) scanf("%d",&a[j]);
        if(have())//每个i只需要和i-1比较即可
        {
            puts("Twin snowflakes found.");
            return 0;
        }
    }
    puts("No two snowflakes are alike.");
    return 0;
}

题解2

hash+最小表示法
有了最小表示法,我们可以提升比对的效率,但是时间不见得快
注意这里的最小表示法还要顺、逆时针分别做一遍,再对"最小"比较出"更小"才行。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int P=99991;

int a[20];
int snow[maxn][10];
int len=0,head[maxn],next[maxn];

int 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()//用最小表示法替代原数列 
{
    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[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 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(int i=1;i<=6;i++) tmp[i]=a[g2-i+1];
        for(int i=1;i<=6;i++) a[i]=tmp[i];
    }
}

bool have()
{
    //判断有无重复 
    zxbsf();
    int h=hash();
    for(int i=head[h];i;i=next[i])
    {
        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()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=6;j++) scanf("%d",&a[j]);
        if(have())
        {
            puts("Twin snowflakes found.");
            return 0;
        }
    }
    puts("No two snowflakes are alike.");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/81514801
今日推荐