一道历史悠久且经典的哈希法例题,哈希法的题目实在是比较少。
暴力枚举的话复杂度显然是是,实际上,如果能提前计算出每个雪花的特性,那么属性不相同的雪花就无需进行比较。相当于我们将雪花分成若干个小组(想想世界杯为什么要分小组赛,而不是32个队都两两比一次),每个小组内部两两比较,这样的话复杂度就会降低。假设我们能把n个数组分成X组,每组数据估算为n/X个,组内两两比较。那么算法复杂度为,当X比较大时复杂度会有明显的降低。
这样分组使用的方法(哈希函数)就很重要,既要保障X的值稍大一点,又要保证相同的雪花一定分在同一组。最简单的就是把雪花的6个角值求和(也可以求乘积),再对X求余,X取值可以设定为某个质数,如果设定为997,那么雪花被分成997组,同理也可以设定为9997或其他值,读者可以尝试把X设置比较小的值,如97看看会否超时。
计算两个雪花是否一致只能用暴力算法,用环处理或者直接枚举位置都可行,此处是直接枚举正反两方向。
#include <iostream>
#include<vector>
typedef long long ll;
using namespace std;
ll n,a[100005][7],mod=99997;
vector<int>v[100010];/**< 动态二维数组v用于存储哈希表,哈希值为X的元素存储在v[X]中,实际上是教材上的链地址法,但是链表太麻烦了,用这种写法可以完美取代链表*/
int hs(int i) /**< 哈希函数 */
{
return (a[i][1]%mod+a[i][2]%mod+a[i][3]%mod+a[i][4]%mod+a[i][5]%mod+a[i][0]%mod)%mod;
}
bool check(int x,int y)
{
int i,j;
for(i=0; i<6; i++)/**< 正反两个方向比较雪花 */
if(a[x][0]==a[y][i]&&a[x][1]==a[y][(i+1)%6]&&a[x][2]==a[y][(i+2)%6]&&a[x][3]==a[y][(i+3)%6]
&&a[x][4]==a[y][(i+4)%6]&&a[x][5]==a[y][(i+5)%6])
return true;
for(i=0; i<6; i++)
if(a[x][0]==a[y][i]&&a[x][1]==a[y][(i+5)%6]&&a[x][2]==a[y][(i+4)%6]&&a[x][3]==a[y][(i+3)%6]
&&a[x][4]==a[y][(i+2)%6]&&a[x][5]==a[y][(i+1)%6])
return true;
return false;
}
int main()
{
int i,j,k,l;
cin>>n;
for(i=1; i<=n; i++)
{
for(j=0; j<6; j++)
scanf("%d",&a[i][j]);
int t=hs(i);/**< 计算哈希值t后,下面循环访问v[t],和之前出现过,且哈希值同样是t的雪花做比较 */
for(j=0; j<v[t].size(); j++)
if(check1(i,v[t][j]))
{
cout<<"Twin snowflakes found.";
return 0;
}
v[t].push_back(i);/**< 比较结束,将i加入对应的哈希表, */
}
cout<<"No two snowflakes are alike.";
return 0;
}
|