代码的优化

      任何一道题,都有许多解法,但是各种方法的时间、空间复杂度各不相同。在原有代码的基础上进行优化,是我们OIer必不可少的素质。现在我就一道弱小的递归题展现整个优化的过程。

 

【题目描述】(rqnoj153)

我们要求找出具有下列性质数的个数(包含输入的自然数n):



先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理:

1.不作任何处理;

2.在它的左边加上一个自然数,但该自然数不能超过原数的一半;

3.加上数后,继续按此规则进行处理,直到不能再加自然数为止.



样例: 输入: 6



满足条件的数为(此部分不必输出)

6 

16

26

126

36

136



输出: 6



【输入格式】

一个自然数:n



【输出格式】

具有以上性质的数的个数:s



【样例输入】

6 

【样例输出】

6

 

      显然,每次取1到n div 2中的一个数,然后进行递归find(x),tot每次加1,直到x=1时无法再分为止。

 

      代码:

 

  
    
1 program number;
2 var
3 n:integer;
4 tot:longint;
5 procedure find(x:integer);
6 var
7 i:integer;
8 begin
9 if x <= 1 then exit;
10 for i: = x div 2 downto 1 do
11 begin
12 inc(tot);
13 find(i);
14 end ;
15 end ;
16 begin
17 readln(n);
18 find(n);
19 inc(tot);
20 writeln(tot);
21 end .
 

 

 

      这种方法看上去很简练,但是这弱小的朴素代码当你输入1000时在10s内已经无法出解(当时硬着头皮交了,结果竟然过了..囧)

      让我们来分析一下。我们是在1到 x div 2中进行递归。但是自然数包括奇数和偶数(显然*.*||),比如500 div 2和501div 2的值是一样的,都是250。然后从250开始以后两个过程是完全相同的。所以看上去完美的朴素递归,竟然有一半过程是没有用的!

      这是我们想到了记忆化搜索。只要记录下来每个数可以扩展的数目,之后再用到就可以直接调用,不用再花时间进行搜索。

 

      代码:

 

  
    
1 program number;
2 var
3 n:integer;
4 f: array [ 1 .. 10000 ] of longint;
5 function find(x:integer):longint;
6 var
7 i:integer;
8 begin
9 if f[x] <> 0 then exit(f[x]); // 当f[x]已经有值时就不用再搜了
10 for i: = 1 to x div 2 do // 在可能的范围内寻找解
11 f[x]: = f[x] + find(i); // 累加,记录以x为原数的可行解
12 f[x]: = f[x] + 1 ;
13 exit(f[x]);
14 end ;
15 begin
16 readln(n);
17 f[ 1 ]: = 1 ; // 初始化f数组
18 writeln(find(n));
19 end .
 

 

 

      记忆化搜索的代码不是很好理解。这是我们想到了动态规划。因为从左到右找,从低位到高位,没有后效性,这样记录1到n所有的解,直接输出a[n]即可。

 

      代码:

 

  
    
1 program number;
2 var
3 a: array [ 1 .. 1000 ] of longint;
4 i,j,n:integer;
5 begin
6 readln(n);
7 for i: = 1 to n do a[i]: = 1 ; // 初始化。就只有n一个数也是一种情况
8 for i: = 2 to n do // 循环n的大小,找出所有n前面的数的解
9 for j: = 1 to i div 2 do // 循环加上的数
10 a[i]: = a[j] + a[i]; // 更新a数组的值
11 writeln(a[n]);
12 end .
 

 

 

      好了,整个优化过程就此完毕。其实代码的优化就是这样的一个过程,去除无用枝,提高时间效率,再简化代码。这样我们的代码就能简洁高效,简单易懂。

 (saltless原创,转载请注明出处)

你可能感兴趣的:(优化)