POJ3349-Snowflake Snow Snowflakes
转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1304831877
大致题意:
在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
为了避免出现大数,这里使用了同余模定理求key
注意,这里的key千万不能用平方和,本来这题时间就很紧凑了,乘法运算更加严重浪费时间,所以采用了连加求key,只要prime足够大,同样能够把地址冲突降低到最低,我取了10n(就是100W)内的最大的素数作为prime, prime=999983
基本做法:
从上面的处理手法能够知道:
当且仅当两片雪花的key值一样时,这两片雪花才有可能相同。
在输入第k个雪花信息的时候,先求出其key值,若在hash[]中key值没有出现过,则直接存放信息。但如果在hash[key]中已经保存有其他地址,说明此前出现过key值相同的其他雪花,这些雪花信息以链表的形式存放在hash[key]中,这时在为第k个雪花信息寻找存放空间的同时,必然在链表中逐一访问这些具有相同key值得雪花,所以我们就能在寻址的同时,顺便逐一比较第k个雪花与这些雪花的信息,一旦发现k与某个雪花是一样的,则标记,然后等待后续输入完成后,直接输出寻找到两片一样的雪花。
但是当所有输入结束后都没有标记过,则说明不存在一样的雪花。
这时肯定又会有同学有疑问:
Key值只能说明两片雪花的叶子长度之和相等,但是不能说明6片叶子分别相等,更加不能说明6片叶子按顺序相等。那么当我们寻找到key值相同的两片雪花时,我们该如何比较两片雪花?
其实是可以的。假设有两片雪花,A 和B
我们固定A,先按顺时针方向比较:
若A0==B0,则按顺序比较A1和B1.........比较A5和B5
只要当出现Ai != Bi,则把B顺时针转动一次,
若A0==B1,则按顺序比较A1和B2.........比较A5和B0
。。。。。
以此类推,直至B转动了5次,若还不相同,则说明这两片雪花在顺时针方向不等。
再比较逆时针方向:
同样固定A,若A0==B5,则按顺序比较A1和B4.........比较A5和B0
只要当出现Ai != B(5-i),则把B逆时针转动一次,
若A0==B4,则按顺序比较A1和B3.........比较A5和B5
。。。。。
以此类推,直至B转动了5次,若还不相同,则说明这两片雪花在逆时针方向不等。
如是者,比较两片雪花最坏的情况为要比较36*2 = 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次就能得到“不存在一样的雪花”的结果。
经过测试发现,prime取100W左右的素数时,key的离散程度是相对比较高的,冲突也就很少,prime再大,对离散化程度影响不大,而且会浪费空间。。
而当prime取10W左右的素数时,出现key值相同的情况达到6K多个,此时Hash的优势根本体现不了。
1 //Memory Time
2 //16696K 3766MS
3
4 #include<iostream>
5 using namespace std;
6
7 const __int64 prime=999983; // 10n内最大的素数(本题n=10W)
8
9 class
10 {
11 public:
12 __int64 len[6]; //6瓣叶子的长度
13 }leaf[100001];
14
15 typedef class HashTable
16 {
17 public:
18 __int64 len[6]; //记录6瓣叶子的长度信息
19 HashTable* next; //用于冲突时开放寻址
20
21 HashTable() //Initial
22 {
23 next=0;
24 }
25 }Hashtable;
26
27 Hashtable* hash[prime+1];
28
29 /*计算第k个雪花的关键字key*/
30
31 __int64 compute_key(int k)
32 {
33 __int64 key=0;
34 for(int i=0;i<6;i++)
35 {
36 key+=(leaf[k].len[i]) % prime;
37 key%=prime; //利用同余模定理计算key,避免出现大数
38 }
39
40 return ++key; //键值后移1位,把key的范围从0~999982变为 1~999983
41 }
42
43 /*从顺时针方向判断两片雪花是否相同*/
44
45 bool clockwise(Hashtable* p,int k)
46 {
47 for(int j=0;j<6;j++) //顺时针转动j格
48 {
49 bool flag=true;
50 for(int i=0;i<6;i++)
51 if(leaf[k].len[i] != p->len[(i+j)%6])
52 {
53 flag=false;
54 break;
55 }
56 if(flag)
57 return true;
58 }
59 return false;
60 }
61
62 /*从逆时针方向判断两片雪花是否相同*/
63
64 bool counterclockwise(Hashtable* p,int k)
65 {
66 for(int j=0;j<6;j++) //逆时针转动j格
67 {
68 bool flag=true;
69 for(int i=0;i<6;i++)
70 if(leaf[k].len[i] != p->len[(5-i-j+6)%6])
71 {
72 flag=false;
73 break;
74 }
75 if(flag)
76 return true;
77 }
78 return false;
79 }
80
81 /*把第k个雪花信息插入HashTable*/
82 /*当插入的位置已存在其他雪花信息时,顺便比较*/
83
84 bool insert(int k)
85 {
86 __int64 key=compute_key(k);
87
88 if(!hash[key])
89 {
90 Hashtable* temp=new Hashtable;
91
92 for(int i=0;i<6;i++)
93 temp->len[i]=leaf[k].len[i];
94
95 hash[key]=temp; //保存key对应的地址
96 }
97 else //地址冲突,开放寻址,顺便比较
98 {
99 Hashtable* temp=hash[key];
100
101 if(clockwise(temp,k) || counterclockwise(temp,k)) //检查雪花是否相同
102 return true;
103
104 while(temp->next) //寻址
105 {
106 temp=temp->next;
107
108 if(clockwise(temp,k) || counterclockwise(temp,k)) //检查雪花是否相同
109 return true;
110 }
111
112 temp->next=new Hashtable; //申请空间,保存新雪花信息
113 for(int i=0;i<6;i++)
114 temp->next->len[i]=leaf[k].len[i];
115 }
116 return false;
117 }
118
119 int main(int i,int j)
120 {
121 int n; //雪花数
122 while(cin>>n)
123 {
124 /*Initial*/
125
126 memset(hash,0,sizeof(hash)); // 0 <-> NULL
127
128 /*Input*/
129
130 bool flag=false; //记录输入过程中是否出现了相同的雪花
131 for(i=1;i<=n;i++)
132 {
133 for(j=0;j<6;j++)
134 scanf("%I64d",&leaf[i].len[j]);
135
136 /*Hash*/
137
138 if(!flag) //当前还没有出现相同的雪花
139 flag=insert(i);
140 //若出现相同的雪花,则还需后续输入,但不再处理
141 }
142
143 /*Output*/
144
145 if(flag)
146 cout<<"Twin snowflakes found."<<endl;
147 else
148 cout<<"No two snowflakes are alike."<<endl;
149
150 }
151 return 0;
152 }