ACM编程Hash及应用
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
// by linle #include "stdio.h" #include "memory.h" int pin[101]; int hash[2000003]; int main() { int a,b,c,d; int i,j,sum; for(i=1;i<101;i++) pin[i] = i*i; while(scanf("%d %d %d %d",&a,&b,&c,&d)!=EOF) { …… } }
if(a>0&&b>0&&c>0&&d>0||a<0&&b<0&&c<0&&d<0) { printf("0\n"); continue; } memset(f,0,sizeof(f)); n=0; for(i=1;i<=100;i++) for(j=1;j<=100;j++) { s=a*t[i]+b*t[j]; p=hash(s); g[p]=s; f[p]++; } for(i=1;i<=100;i++) for(j=1;j<=100;j++) { s=-(c*t[i]+d*t[j]); p=hash(s); n+=f[p]; } printf("%d\n",n*16);
Hash表简介——基本操作
Hash表初始化(0或-1或其它) 哈希函数运算 插入元素(包含冲突解决) 定位(需考虑可能冲突的情况)
总结:
Hash函数评价标准:
低冲突率 易于编码 优点:数据存储和查找效率高 (几乎是常数时间) 缺点:消耗较多内存(内存很便宜哪~) 查找元素是否属于集合 搜索中的状态表示
HDOJ-1496参考代码(2)
// by laili #include<stdio.h> #include<memory.h> #define MAX 50021 int f[MAX],g[MAX]; int hash(int k) { int t=k%MAX; if(t<0) t+=MAX; while(f[t]!=0&&g[t]!=k) t=(t+1)%MAX; return t; } int main() { int a,b,c,d,p,i,j,s,n,t[101]; for(i=1;i<=100;i++) t[i]=i*i; while(scanf("%d%d%d%d",&a,&b,&c,&d)>0) { …… } }
参考源码(HDOJ-1800)
// by linle #include "stdio.h" #include "memory.h" #define MAXN 7003 inline int ELFhash(char *key) { unsigned long h = 0; unsigned long g; while( *key ) { h =( h<< 4) + *key++; g = h & 0xf0000000L; if( g ) h ^= g >> 24; h &= ~g; } return h; } int hash[MAXN],count[MAXN]; int maxit,n;
Hash函数特点:
Hash主要应用:
Hash-应用基础
第一部分
整数Hash
例1 (HDOJ-1496 Equations)
Consider equations having the following form: a*x12+b*x22+c*x32+d*x42=0 a, b, c, d are integers from the interval [-50,50] and any of them cannot be 0. It is consider a solution a system (x1,x2,x3,x4) that verifies the equation, xi is an integer from [-100,100] and xi != 0, any i ∈{1,2,3,4}. Determine how many solutions satisfy the given equation.
Output
对每组测试数据按从大到小的顺序输出前m大的数。
初步分析
题目特点: 数据量大 数据在一定范围
思考:
常规算法的缺陷? 是否可以将“数据值”和“存储位置” 做某种对应?
经典所在: 存储完毕,则排序完毕!
再思考(1425加强版):
原题描述: 第二行包含n个各不相同,且都处 于区间[-500000,500000]的整数 加强版(思考): 如果整数允许相同怎么处理?
相关练习
HDOJ-1425 HDOJ-1800 HDOJ-1496 HDOJ-1228 HDOJ-1043
Sort Flying to the Mars Equations A+B Eight
附:常用字符串Hash(1)
inline int ELFhash(char *key) { unsigned long h = 0; unsigned long g; while( *key ) { h =( h<< 4) + *key++; g = h & 0xf0000000L; if( g ) h ^= g >> 24; h &= ~g; } return h; }
经典提升
避免字符串比较操作的方法: 多次Hash~ 具体方法… 参考: http://hi.baidu.com/heiyeshuwu/blog/it em/3853baa1ed68b488471064b4.html http://acm.hdu.edu.cn/forum/read.php? tid=6567
例3—(HDOJ-1043 Eight)
经典八数码问题:
123 x46 758
HDOJ-1043题目分析
是否有解的判断:逆序数的奇偶性不变 说明:上一行的确切说明应该是(略) 算法主体:BFS 本讲关注:搜索状态如何表示(哪些方法)? 经典方法:排列数的Hash表示 优点:
Байду номын сангаас
HDOJ-1496题目分析
题意简单(类似“百钱买百鸡”问题) 传统方法是(几重循环)?复杂度? 上述方法如何判断两端相等?(查找?) 可行方案(两重循环+Hash存储查找) Hash表大小?
HDOJ-1496参考代码(1)
Hash表简介——基本原理
哈希表(散列表)的基本原理: 使用一个下标范围比较大的数组来存储元 素,一般通过设计一个函数(哈希函数, 即散列函数),使得每个元素的关键字都 与一个函数值(即数组下标)相对应,然 后用该数组单元来存储对应元素。
Hash表简介——函数构造
无定式,方法很多 最常见的方法:除余法 H(k ) = k mod p (p一般选取适当大的素数) 常用的经典字符串Hash后面介绍
ACM程序设计
第十四讲
Hash及应用
(Hash & Application)
导引问题 (HDOJ-1425 sort )
Problem Description
给你n个整数,请按从大到小的顺序输出其中前m大 的数。
Input
每组测试数据有两行,第一行有两个数n,m (0<n,m<1000000),第二行包含n个各不相同,且 都处于区间[-500000,500000]的整数。
Hash-应用重点
第二部分
字符串Hash
例2—(HDOJ-1800)
Flying to the Mars
HDOJ-1800题目分析
除去马甲,本题的本质是——求相同级 别(level)的人最多是几个。 如果level的范围不大的话(64位整数可 以表示)——本题很简单,简单贪心 本题的难点:level的范围较大,需用大 数或者字符串比较(去首0) 效率较高、编程简单的方法:Hash! 此外,字典树也是不错的选择
HDOJ-1496经验之谈
“两层循环最多只可能产生10000个不 同的结果,开200W的数组将会浪费很多 初始化的时间,所以开小数组+处理冲突 会比较好.”——by LaiLi 具体见论坛讨论: http://acm.hdu.edu.cn/forum/read.php ?tid=3276&fpage=0&toread=&page=2
if( (a>0 && b>0 && c>0 && d>0 )|| (a<0 && b<0 && c<0 && d<0) ) { printf("0\n"); continue; } memset(hash,0,sizeof(hash)); for(i=1;i<=100;i++) for(j=1;j<=100;j++) hash[a * pin[i] + b * pin[j] + 1000000]++; sum = 0; for(i=1;i<=100;i++) for(j=1;j<=100;j++) sum += hash[-(c * pin[i] + d * pin[j]) + 1000000]; printf("%d\n",sum*16);
存储状态的空间较小(多少足矣?) 效率高 编码方便
八数码状态的Hash表示
/* 八数码的经典哈希 求其排列数 功能:求一个数组是第几个字典排列(假设每个元素都不同) */ int fact[9]={1,1,2,6,24,120,720,5040,40320}; int hash(char s[]) { int i,j,n,c; for(i=n=0;i<8;++i) { c = 0; for(j=i+1;j<9;++j) if(s[j]<s[i]) ++c; n += c*fact[8-i]; } return n; }
inline void hashit(char *str) { int k,t; while( *str == '0' ) str++; k = ELFhash(str); t = k % MAXN; while( hash[t] != k && hash[t] != -1 ) t = ( t + 10 ) % MAXN; if( hash[t] == -1 ) count[t] = 1,hash[t] = k; else if( ++count[t] > maxit ) maxit = count[t]; } int main() { char str[100]; while(scanf("%d",&n)!=EOF) { memset(hash,-1,sizeof(hash)); for(maxit=1,gets(str);n>0;n--) { gets(str); hashit(str); } printf("%d\n",maxit); } }
Hash表简介——冲突
由于不能够保证每个元素的关键字与函 数值是一一对应的,因此很有可能出现 如下情况: “对于不同的元素关键字,Hash函数计 算出了相同的函数值”,这就是产生了 所谓的“冲突”。 换句话说,就是Hash函数把不同的元素 分在了相同的下标单元。
Hash表简介——冲突解决
方法很多~
常用方法:线性探测再散列技术 即:当 h(k)位置已经存储有元素的时候,依 次探查 (h(k)+i) mod S, i=1,2,3…,直到找到 空的存储单元为止。其中, S为 数组长度。 特别地,如果将数组扫描一圈仍未发现空单 元,则说明哈希表已满,这会带来麻烦,但 是,该情况完全可以通过扩大数组范围来避 免。