代码的优化

      任何一道题,都有许多解法,但是各种方法的时间、空间复杂度各不相同。在原有代码的基础上进行优化,是我们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原创,转载请注明出处)

posted on 2010-08-06 11:20  saltless  阅读(392)  评论(0编辑  收藏  举报

导航