problem
- 有n片雪花,每片有6个脚,每个脚有一个长度。
- 两片雪花是一样的当且仅当每个脚的长度顺序都一样(顺逆时针和开始位置不管)
- 求n片雪花中是否有一样的雪花。
solution
维护一个哈希表
- 定义Hash(a1,a2,..a6) = (sum(a1..a6)+ji(a1..a6))%P
- 对于两片雪花,当且仅当他们的hash值相同时他们相同.期望复杂度O(N(N/P)^2)
codes
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 100010, P = 99991;
int n, snow[maxn][6];
bool equal(int *a, int *b){
for(int i = 0; i < 6; i++){
for(int j = 0; j < 6; j++){
int eq = 1;
for(int k = 0; k < 6; k++)
if(a[(i+k)%6] != b[(j+k)%6])eq = 0;
if(eq)return 1;
eq = 1;
for(int k = 0; k < 6; k++)
if(a[(i+k)%6] != b[(j-k+6)%6])eq = 0;
if(eq)return 1;
}
}
return 0;
}
int tot, head[maxn], Next[maxn];
int H(int *a){
int sum = 0, mul = 1;
for(int i = 0; i < 6; i++){
sum = (sum+a[i])%P;
mul = (long long)mul*a[i]%P;
}
return (sum+mul)%P;
}
bool insert(int *a){
int key = H(a);
for(int i = head[key]; i; i = Next[i])
if(equal(snow[i],a))return 0;
tot++;
memcpy(snow[tot],a,6*sizeof(int));
Next[tot] = head[key];
head[key] = tot;
return 1;
}
int main(){
int n; cin>>n;
for(int i = 1; i <= n; i++){
int a[10];
for(int i = 0; i < 6; i++)scanf("%d",&a[i]);
if(!insert(a)){
printf("Twin snowflakes found.
");
return 0;
}
}
printf("No two snowflakes are alike.
");
return 0;
}
Hash表
介绍
1、哈希算法通过一个哈希函数H,将一种数据(包括字符串,数组)转化为能用变量表示,或是直接可作为数组下标的数(这样就可以O(1)判断是否存在)。
2、通过哈希函数转化的数值称为哈希值。
3、因为值域变简单,范围变小,可能造成两个原始信息被映射为相同值,称之为冲突。
具体
1、对于哈希函数,我们有以下几种常见的构造方法:
(a)除余法
选择一个适当的模数b,令H(key) = key%b。
b一般选择比较大的质数,减少冲突的几率。
(b)乘积取整法
选择一个(0,1)中的实数A, 令H(key) = M(A*key%1)。
即,key*A取小数部分乘以哈希表大小M再向下取整。
(c)基数转换法
将key看做为另一种进制的数,把他转为对应10进制数,再用除余法对他取余。
字符串哈希一般采用的方法。
2、对于冲突,我们一般采用开散列的方式解决:
对每一个哈希值开一个链表(相当于邻接表),把所有等同于同一哈希值的数字都记录下来,查找的时候遍历对应的链表。
这样期望复杂度为O(1),实际可能变大,但也是常数级。