题意
给你n朵雪花,每朵雪花有6片花瓣,每片花瓣有一个权值。花瓣的权值可以从任意位置的顺时针或逆时针来读,判断是否有两朵相同的雪花。
题解1
暴力hash
我们约定,一朵雪花的hash值为,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;
}