当前位置:   article > 正文

题目1380:lucky number_1380组成的六位密码有多少个

1380组成的六位密码有多少个

转载请注明文本链接 http://blog.csdn.net/yangnanhai93/article/details/40441709


题目链接地址:http://ac.jobdu.com/problem.php?pid=1380

第一次这么正式的去写这个ACM的算法的博客,感觉这个题目给我的触动有点大,很多时候更多的算法需要观察,需要细心的品味题目,才能够有:“啊哈,算法”——《编程珠玑》

先说下自己的惯性思路吧,因为这个题目就是很明显一个统计的问题,所以,我一上来的就是尽量的去减少内存开销,所以我采用的是:

在我看来这个应该是没有问题的,因为int 4字节 char 1字节,因为m%n!=0,且n>=2,所以这个map的元素中的个数是小于等于5*5*10^5=2.5M的,然后我把代码精简,希望得到更小的开销,但是发现和使用int short作为map的开销是一样的,这个不能理解。

  1. #include <stdio.h>
  2. #include <map>
  3. using namespace std;
  4. int main()
  5. {
  6. map<int,char> mp;
  7. map<int,char>::iterator key;
  8. int n,m,num;
  9. while(scanf("%d%d",&n,&m)!= EOF)
  10. {
  11. mp.clear();
  12. while(m--)
  13. {
  14. scanf("%d",&num);
  15. key = mp.find(num);
  16. if(key == mp.end())
  17. mp.insert(pair<int,char>(num,'1'));
  18. else if(key->second<(n+'0'))
  19. key->second++;
  20. }
  21. key=mp.begin();
  22. while(key!=mp.end()&&key->second==(n+'0'))
  23. key++;
  24. printf("%d\n",key->first);
  25. }
  26. return 0;
  27. }
  28. /**************************************************************
  29. Problem: 1380
  30. User: vincent_ynh
  31. Language: C++
  32. Result: Memory Limit Exceed
  33. ****************************************************************/

第二个稍微能够减少空间的思路是,发现只需要4位存储就可以了,然后用位去存储,这里,因为我个人的理解题目失误,误认为是1-10^6个数,然后写了一个用位存储的代码,很少写位操作的代码,写得比较难看,但是觉得作为一个精益求精的coder还是需要更多的使用的,《编程珠玑》中的第一个电话本的例子给我的触动特别大,让我感受到代码原来可以如此的巧妙,好吧,扯远了,这里贴一下,如果数据的范围小的时候,也可以使用如下位存储代码:

  1. #include <stdio.h>
  2. #include <memory.h>
  3. int main()
  4. {
  5. int A[300000],B[2]={15,240};
  6. int n,m,num;
  7. while(scanf("%d%d",&n,&m)!=EOF)
  8. {
  9. memset(A,0,sizeof(A));
  10. while(m--)
  11. {
  12. scanf("%d",&num);
  13. int tmp=(num-1)/2;
  14. int x=(num-1)%2;
  15. if((A[tmp]&B[x])>>(4*x)<=n)
  16. {
  17. A[tmp]=A[tmp]+(1<<(4*x));
  18. }
  19. }
  20. for(int i=1;;i++)
  21. {
  22. int tmp=(i-1)/2;
  23. int x=(i-1)%2;
  24. if(((A[tmp]&B[x])>>(4*x))%n!=0)
  25. {
  26. printf("%d\n",i);
  27. break;
  28. }
  29. }
  30. }
  31. return 0;
  32. }
  33. /**************************************************************
  34. Problem: 1380
  35. User: vincent_ynh
  36. Language: C++
  37. Result: Runtime Error
  38. ****************************************************************/
最后就是该题我能想到的目前最优的算法了:

我们对每一个数进行二进制编码,得到的是32 位的数,如果这个数出现了n次,那么这样的编码就应该出现了n次,如果对n取余,那么恰好就把这些出现n次的数消除了。想通了这个,然后接下来就是没有出现n次的,那么就是出现了m%n次的,当然可以是m%n+x*n,不影响的,在剩余的数组中,就剩下了lucky number的编码(每 位乘以m%n),所以,我们只需要把数组中对每一位取余(%n)同时,把非0位,改为1,位不变,最后把该数组转换成整数就可以了。

代码的主要思路我们通过例子能够发现:

2 5

1 1 2 2 3

结果是3

编码得到00000001 00000001 00000010 00000010 00000011(因为数字比较小,省略了前面3位即24个0)

整合进数组之后00000033 取余之后为00000011 

然后把非0的转换为1,得到00000011

把数组转换为整数为3,所以结果为3


这里补充一下“位与(&)”操作,位与(&)操作是对数字进行二进制编码,对应位如果同为1,则为1,否则为0


实际代码如下:

  1. #include <stdio.h>
  2. #include <memory.h>
  3. int main()
  4. {
  5. int A[32],m,n,num;
  6. while(scanf("%d%d",&n,&m)!=EOF)
  7. {
  8. memset(A,0,sizeof(A));
  9. for(int j=0;j<m;j++)
  10. {
  11. scanf("%d",&num);
  12. for(int i=0;i<32;i++)
  13. {
  14. A[i]=A[i]+(num&1);
  15. A[i]=A[i]%n;
  16. num=num>>1;
  17. }
  18. }
  19. int tmp=m%n,result=0;
  20. for(int i=0;i<32;i++)
  21. {
  22. if(A[i]%n==tmp)
  23. result+=1<<i;
  24. }
  25. printf("%d\n",result);
  26. }
  27. return 0;
  28. }
  29. /**************************************************************
  30. Problem: 1380
  31. User: vincent_ynh
  32. Language: C++
  33. Result: Accepted
  34. Time:1620 ms
  35. Memory:1020 kb
  36. ****************************************************************/


欢迎任何想学习算法的人交流:vincent_ynh@163.com


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/75780
推荐阅读
相关标签
  

闽ICP备14008679号