哈希表入门题目总结(HDU 1280、1425、2027、3833、1496、2648 POJ 1200)

来源:互联网 发布:网络清洁器 编辑:程序博客网 时间:2024/05/17 12:20

今天做了一天的哈希表题目,虽然不是哈希算法,但是感觉先把这哈希表搞定了,再学哈希算法的时候会快一些,所以今天搞得现在都有点头痛了,看了一天的电脑……可怜

快要睡觉了,整理一下今天的成果。整理一下题目与思想,明天学哈希算法的时候应该会有些帮助。

先推荐一个哈希入门网站,还没做题之前,我把这网站关于哈希的都看了个遍,有些明白了才开始做题的:http://wenku.baidu.com/view/98383d34f111f18583d05a81.html

总的来说,在很多数据面前,如果要处理,我们自然会想到用数组,链表,或者线段树去处理,当然,这些不失为一种好的解决办法。不过数组寻址容易,插入和删除困难,而且如果n太大,遍历会超时;链表插入删除容易,但是遍历很慢;线段树结构复杂,在很多数组面前,处理和遍历也很烦,有时也是会超时的。所以人们就想到了这个哈希算法解决这个问题。

由上面的解释大概也明白了哈希是怎么做出来的了。就是把那些数据给上地址,结合链表的特点,就能做到寻址又快,插入删除查询又快的方法了。不过这里说太多了,这应该是哈希算法的时候才会用得到的。哈希表几乎都是和地址有关的,把地址给搞定了,题目就做得很简单了,时间也会少很少了。

其实哈希就像字典一样,把那些地址都给确定好了,然后查找的时候就快了。看上面给出的那个网站,里面有更详细的解释……

做哈希的原因:这段时间做题发现自己对数据处理很弱,在数据很多的情况下,总是会超时……而哈希在这方面出类拔萃,不得不学了!

第一道练习题:HDU 1280 前m大的数

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  #define M 1000005  typedef long long ll;  using namespace std;  int a[3005],b[10005];  int main()  {      int n,m,i,j;      while(cin>>n>>m)      {          mem(b,0);          int k=0;          for(i=0; i<n; i++)              cin>>a[i];          for(i=0; i<n; i++)              for(j=i+1; j<n; j++)                  b[a[i]+a[j]]++;  //直接把相加的数当作地址,也就是下标        for(i=10000; i>0&&m>0;)          {              if(!b[i]) {i--;continue;}              if(k) cout<<' '<<i;    //因为空格没处理PE了一发              else cout<<i;              k=1;              b[i]--;   //相加可能有相同的,而输出可以有重复的,和北航校赛那题太像了!              m--;          }          cout<<endl;      }      return 0;  } 

第二题 HDU 1425 sort

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  typedef long long ll;  using namespace std;  #define M 500000  int hash[M*2+1];  int main()  {      int n,m;      while(cin>>n>>m)      {          int a,i,j,k=0;          mem(b,0);          for(i=0; i<n; i++)          {              scanf("%d",&a);              hash[M+a]=1;    //和上面那题差不多,+M的原因就是防止下标为负的情况        }          for(i=M*2; i>=0&&m>0; i--)          {              if(!hash[i]) continue;              if(k) cout<<' '<<i-M;              else cout<<i-M;              k=1;              m--;          }          cout<<endl;      }      return 0;  } 

第三题:HDU 2027 统计元音

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  typedef long long ll;  using namespace std;  #define M 100005  char s[101],hash[6]={'a','e','i','o','u'};  int main()  {      int t;      cin>>t;      getchar();      while(t--)      {          int i,a[200]={0};          gets(s);          for(i=0;i<strlen(s);i++)              a[s[i]]++;          for(i=0;i<5;i++)              printf("%c:%d\n",hash[i],a[hash[i]]);  //元音字母本身作为下标直接查找        if(t) puts("");      }      return 0;  } 

第四题:HDU 3833 YY's new problem

思路:这题实在是苦死我了,刚开始看题目不知道怎么意思。有点让人产生歧义。又看了别人的解释,自己还是没明白什么意思,看得自己都困了睡了一下起来……发现就是给出一组数,如果有有 a-b=b-c 的话就是Y,否则是N。当然a 的位置在b 的前面,c 的位置在b 的后面。这样就明白了。唉……读题好慢……题目给出那个permutation,让自己产生歧义理解了好久。

不过这题当然用哈希就快了,如果用平常的方法的话,那就是3个for循环才能搞定了,虽然能搞定,但是n<=10000,这也会超时……因为题目的重要的字眼是位置,他们的位置是a,b,c 。而哈希的优势正好在于处理位置地址方面无与伦比,那这题用哈希做的话,当然是最好的解法。那要具体怎么做呢??……

比如一组数列:6 3 2 5 4 1 (题目给出的数是不会重复的)设输入的数为s。

输入的数直接做为hash数组的下标,数组等于1,表明这个数出现过。即hash[6]=1,然后由题目的条件,即6为b,则要找出a在不在前面出现过,如果出现过的话,那这个数列的后面肯定还有一个数c 符合a-b=b-c,转换一下就是2*b=a+c。就是小学的这个公式。现在问题来了,要怎么查找a最快呢,能不能直接一个判断就可以了呢??哈希说当然可以……

if(hash[s-j]+hash[s+j]==1)   就有了这个式子。例如输入6的时候判断一下j=1->s-1(j从1到s-1)。如果s-j出现过,那么s+j也会在s后面出现的,就是Y了;如果s+j出现过,那么s-j也会在s后面出现的,也是Y了;如果都没有出现过,那j 自增再判断……

比如当输入s=2 时,j=1->s-1,j=1时,s+j==3出现过,且s-j==1没出现过,这就正好符合了。2*2==3+1 正好是题目给出的式子的条件。s就是其中位数而已,如此判断岂不快唉……

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  typedef long long ll;  using namespace std;  #define M 100005  int hash[10003];  int main()  {      int t;      scanf("%d",&t);      while (t--)      {          int n,flag=0,i,j,s;          mem(hash,0);          scanf("%d",&n);          for (i = 1; i <= n; i++)          {              scanf("%d",&s);              hash[s]=1;              if (flag == 0)              {                  for(j=1;j<a&&j+s<=n;j++)     //b=s                  {                      if(hash[s-j]+hash[s+j]==1)   //判断a或者c是否出现过,只能出现一个才符合,比如:3 1 2时,2前面都现在了3 和1  ,这不符合                      {                          flag=1;                          break;                      }                  }              }          }          if(flag) printf("Y\n");          else printf("N\n");      }      return 0;  }  

第五题:HDU 1496 Equations

题意:就是给出a,b,c,d然后求出x1,x2,x3,x4有多少组解……

思路:这题还是收获很大啊……以前真不敢这么想,也不敢这么用过数组……哈希的思想太猛了!!

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  typedef long long ll;  using namespace std;  #define M 2000000  int s[101],hash[M+3];  int main()  {      int a,b,c,d,i,j,sum;      for(i=1;i<101;i++)          s[i]=i*i;      while(cin>>a>>b>>c>>d)      {          if(a>0&&b>0&&c>0&&d>0||a<0&&b<0&&c<0&&d<0)  //因为x那项永远是正数,如果系数都为正或者为负的时候明显不等于0          {              printf("0\n");              continue;          }          mem(hash,0);          sum=0;          for(i=1;i<101;i++)              for(j=1;j<101;j++)                //也可以先求前1项或者前3的,但是3个循环肯定比2个的时间多,所以这就是为什么先求前两项的原因                  hash[a*s[i]+b*s[j]+M/2]++;    //(加上M/2的原因是防止下标为负,先求前两项,例如a*s[i]+b*s[j] == 5             for(i=1;i<101;i++)              for(j=1;j<101;j++)                  sum+=hash[-(c*s[i]+d*s[j])+M/2];   //与前面例如对应:如果c*s[i]+d*s[j] == -5  那么相加正好等于0,符合题解,正好求出x,故项数在前面加号就是5啦,5那项有多少,-5与之对应的就有多少解……          printf("%d\n",sum*16);   //因为 x 是平方,所以正负数的平方都一样,一个x就会有两个解了,4个x当然就有2^4=16个解    }      return 0;  }

第六题:HDU 2648 shopping

这题刚开始确实不会,研究了好久,还是不会。迫不得已看了下别人的做法,实在高明啊!!

又是一道哈希实质题。地址哈希,唉,这思想也不知道哪个大神想出来的,佩服啊……见识了奋斗

#include<iostream>  #include<cstdio>  #include<algorithm>  #include<cstring>  #include<string>  #include<cmath>  #include<set>  #include<map>  #include<queue>  #include<vector>  #include<stack>  #include<ctime>  #include<cstdlib>  #define mem(a,b) memset(a,b,sizeof(a))  typedef long long ll;  using namespace std;  #define M 2000000  int hash[10003];  int main()  {      int n,m,i,j,k,w,a;      string s;      while(scanf("%d",&n)!=EOF)      {          map<string,int>q;  //拿map中字符串映射地址,就转换成了整形了,哈希只要能转成整形的怎么都好讲了          mem(hash,0);          for(i=0; i<n; i++)          {              cin>>s;              q[s]=i;   //字符串对应地址,地址从0开始编号              if(s=="memory") w=i;   //记住目标字符串的地址          }          scanf("%d",&m);          for(i=0;i<m;i++)          {              int sum=0;              for(j=0; j<n; j++)              {                  cin>>a>>s;                  hash[q[s]]+=a;   //哈希数组加权值,地址相当于哈希数组下标,简单明了              }              for(k=0; k<n; k++)                  if(hash[w]<hash[k]) sum++;  //然后打擂台法找出多少个就行了              printf("%d\n",sum+1);          }      }      return 0;  }

第七题 POJ 1200

这题和上面的一样,也是哈希表……只要找到这个串里能唯一表示地址的那个计算式就行了……

#include <bitset>#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <utility>#include <deque>#include <vector>#include <list>#include <queue>#include <string>#include <complex>#include <cstring>#include <map>#define pi acos(-1.0)using namespace std;typedef long long ll;#define M 16000000char s[M];int a[100],hash[M];int ;int main(){    int N,NC,num=0,len,i,j,ans=0,sum=0;    scanf("%d%d",&N,&NC);    scanf("%s",&s);    len=strlen(s);    for(i=0; i<len; i++)        if(!a[s[i]]) a[s[i]]=++num;  //其实不从0开始也行的,只不过从0开始,后面计算的时候hash数组的下标就比较小了,这样不会越内存了    for(i=0; i<len-N+1; i++)    {        sum=0;        for(j=i; j<i+N; j++)            sum+=sum*NC+a[s[j]];   //也可以不乘以NC,但是一定要乘以一个>=NC的数,因为这样才能唯一的表示一个字符串转换成整形数组的下标地址,而NC最小嘛        if(!hash[sum])        {            ans++;            hash[sum]=1;        }    }    printf("%d\n",ans);    return 0;}



0 0
原创粉丝点击