网易2016笔试(2)

来源:互联网 发布:vue.js实战教程视频 编辑:程序博客网 时间:2024/05/16 09:35

有n个信封,包含n封信,现在把信拿出来,再装回去,要求每封信不能装回它原来的信封,问有多少种装法?

给定一个整数n,请返回装发个数,为了防止溢出,请返回结果Mod 1000000007的值。保证n的大小小于等于300。

测试样例:2   返回 1

           一开始我以为是卡特兰数问题:卡特兰数应用
          考虑半天,但是也无法列出卡特兰数的公式。安静下来发现,h(0)=h(1)=0,不满足卡特兰数条件。于是乎就想请教大神。大神给了个思路:
       计算f(n),假设1,2,3,4,......n。那么n若放在位置i,可以分两种情况考虑。i放在位置n和i不放在位置n。为什么这么考虑呢?因为如果i放在n,那么剩下就是f(n-2),否则的话就是f(n-1)。第一种情况你很容易理解,i和n都放好了,剩下的就是n-2个数不放在原来的信封里。但是i如果不放在n位置,剩下n-1个数,其中i放哪都可以啊,和题目中每个信封不放在原信封条件不同啊。很好,i是可以放在任何位置,但前提假设了这种情况是i不放在n位置。这样这n-1个数,除了i,其余的限制是不放在原信封,i的限制是不放在n位置。所以等价这其实就是f(n-1)。
        问题到这,用递归计算就太简单了。代码如下:
        
int countWays(int n) {        // write code here        if(n<2)return 0;        if(n==2)return 1;        return (n-1)*(countWays(n-1)%1000000007+countWays(n-2)%1000000007);    }
      
       问题求得的结果没得问题,但是时间使用超过3000ms,远远超过限制时间。
           但也可以猜想到,就是上述代码重复计算了太多值。于是乎,就改用循环。代码如下:
       
int countWays(int n) {        // write code here       int a=0;       int b=1;        if(n<=1)return a;        if(n==2)return b;        long result;        long x=0;        long y=1;        for(int i=3;i<=n;i++)            {            result=(i-1)*(x+y)%1000000007;            x=y;            y=result;        }        return result;    }
      这样就很好了,根据n的大小,从头到尾计算一遍,直至f(n)。
      总结:在一个序列上递归就很容易产生重复计算的问题,造成代码效率不高。同时一开始考虑问题总想着套路,这样并不好。有思路有解法才能利用公式等工具解决问题。先想解法,再看是不是公式,再解题。
0 0
原创粉丝点击