jdoj 1402 特殊的数 代码及分析

    这是jobdu的好题,网上的解说都是用位运算的,但是我现在对位运算还不太了解,所以我的代码是用对数组的压缩使用来实现对小数目个数的统计的,也因此对存储空间的利用是不够充分的。

原理解释:一个int是4个字节组成的。在这题里面,如果开10^6的数组,4*10^6B≈4MB,这还没加上运行程序命令所需要的内存。由此可见,这样开数组是必定MLE的。但是我们可以发现,对于这题而言,每个数中我们所需要的状态只有0,1,2三种,于是我就想到数组在内存中的存储方式,并模仿它建立起一个大小只有50000的int数组。10^6 / (log(2^31) / log(3))<50000,由于数的范围是1~10^6,所以令数(a-1)/50000得到的值作为行位置,(a-1)%50000得到的值作为列位置,由此,一个100w的数组就被压缩成一个20行5w列的数组了。我的代码优化了输出,限定了输入的数的范围,最后遍历检测输出个数为1的数。

    这种方法并不高效,我最多只能把它优化到1104kb 1610ms的运行内存和运行时间。

    下面是我的代码,供大家参考:

View Code
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<math.h>
 5 #define MAX 50000   //决定数组大小
 6 #define POW 3    //每个数的状态数量
 7 
 8 
 9 unsigned int cnt[MAX];
10 
11 unsigned int ten(int pow)   //这里我要特别提醒的是,C里面的pow函数返回值强制变成int后,可能得到的不是我们所想要的那个数
12 {
13     unsigned int back;
14 
15     back=1;
16     while(pow--)back*=POW;
17 
18     return back;
19 }
20 
21 int amount(int a) //返回数a的个数
22 {
23     return (cnt[(a-1)%MAX]/ten((a-1)/MAX))%POW;
24 }
25 
26 int main()
27 {
28     int i, c;
29     int n, m;
30     int min, max, maxn, minn;
31 
32     while(scanf("%d", &n)!=EOF)
33     {
34         memset(cnt, 0sizeof(cnt));
35         max=maxn=c=0;min=minn=9999999;
36         while(n--)
37         {
38             scanf("%d", &m);
39             if(min>m)min=m;
40             if(max<m)max=m;
41             if(amount(m)!=2)cnt[(m-1)%MAX]+=ten((m-1)/MAX); //数的个数≥2就不用继续加了
42         }
43         for(i=min; i<=max; i++)
44             if(amount(i)==1)
45             {
46                 if(!c)minn=i;
47                 c++;
48                 if(maxn<i)maxn=i;
49             }
50         if(c)printf("%d\n", c);
51         else {printf("0\n");continue;}
52         for(i=minn; c!=1; i++)
53             if(amount(i)==1){printf("%d ", i);c--;}
54         for(; i<=maxn; i++)
55             if(amount(i)==1)printf("%d\n", i);
56     }
57 
58     return 0;
59 
60 }

written by Lyon

  

 

原文地址:https://www.cnblogs.com/LyonLys/p/jdoj_1402_Lyon.html