转载自博客园
何为”打表”呢,说得简单点就是:
有时候与其重复运行同样的算法得出答案,还不如直接用算法把这组数据所有可能的答案都枚举出来存到一个足够大的容器中去-例如数组(打表),然后再输入数据的时候,直接遍历容器,检索这个数据是否有题意要求的结果。
•举一个几乎所有程序员都知道的简单例子= =: 求素数(POJ 1595)-Prime cuts
这一题大意是给出 多组N(1~1000)和C,让你从N内素数的中间项向外扩展C个素数,比如给出7 1,素数有5个(注意此题出题人坑爹得让1作为”素数”) 1,2,3,5,7,那么应输出7 1:3 5 7(注意输出格式)
特别的,偶数个素数输出2c-1个,奇数个则输出2c个。
那么有几点我们是需要分析的:
- •N的数据范围小,但可能数据量较大,看起来貌似只有1000内的素数,但如果给出上万组数据,用蛮横的办法,每次都把素数求解出来然后去统计中间值,然后扩展,显然是不明智的,容易TLE。
- •应当注意特判:C如果大于实际素数范围,那么我们应当输出所有素数,这个地方没有处理好,容易WA。
- •注意输出扩展的素数的上下限,也注意不要超出最大素数(<=1000),discuss区有人说测试数据可能大于1000可能是他自己没有处理好这个问题。
第二,三点是细节问题,但是第一点则是算法优化问题了,常见的素数算法优化就是:打表法 and 筛选法。
- •打表法:在处理问题前先把所有可能的素数单独用数组做标记,然后在处理问题时查找相应的素数就行了,省去了重复统计素数的时间。
- •筛选法:标记范围内所有合数(基本思路就是:有两数:i,j(i,j>=2),那么i*j一定是合数,这样当 i 在遍历到这些合数时就可以跳过不进行运算了)
code如下:
1 //素数打表(筛选法)
2 //Memory: 140 K,Time: 0 Ms
3 #include<iostream>
4 #include<cstdio>
5 #include<cmath>
6 using namespace std;
7
8 #define MAX 1005
9
10 int prime[MAX],pl;
11 int num[MAX];
12
13 /*筛选法素数打表*/
14 void prime_number(int max_num)
15 {
16 int i,j;
17 int k = (int)sqrt(1.0*max_num); //开方-四舍五入
18
19 /*标记所有合数*/
20 for(i=2;i <= k;i++)
21 {
22 if(!num[i])
23 for(j = 2*i; j<=max_num; j+=i)
24 num[j] = 1;
25 }
26
27 /*打表*/
28 pl = 1; //素数计数器
29 for(i=1;i <= max_num;i++) //ps:此题"素数"包括1
30 {
31 if(!num[i])
32 prime[pl++] = i;
33 }
34 return;
35 }
36
37 int main()
38 {
39 int n,c;
40 int i;
41
42 prime_number(MAX); //先打好最大素数表
43
44 while(~scanf("%d%d",&n,&c))
45 {
46 /*找到素数区间*/
47 for(i = 1; i < pl; i++)
48 {
49 if(n < prime[i])
50 break;
51 }
52 int maxp = i-1;
53
54 printf("%d %d:",n,c);
55 if(maxp%2) //奇数个
56 {
57 int mid = (maxp+1)/2;
58 if(2*c-1 > maxp)
59 c = mid;
60
61 for(i = mid-c+1; i <= mid+c-1; i++)
62 printf(" %d",prime[i]);
63 }
64 else //偶数个
65 {
66 int mid = maxp/2;
67 if(2*c > maxp)
68 c = mid;
69
70 for(i = mid-c+1;i <= mid+c; i++)
71 printf(" %d",prime[i]);
72 }
73 printf("\n\n");
74 }
75
76 return 0;
77 }