2020阿里实习校招算法笔试题

使用Python2.7.3

题目1: 队伍选择

题目描述:现有n个人,要从这n个人中选任意数量的人组成一只队伍,再在这些人中选出一名队长,求不同的方案对 1 0 9 + 7 10^9+7 109+7取模的结果。如果两个方案选取的人的集合不同或选出的队长不同,则认为这两个方案是不同。
输入描述

一行一个数字n。
1 < = n < = 1 0 9 1<=n<=10^9 1<=n<=109

输出描述

一行一个数字表示答案

示例1
输入

2

输出

4

说明

4种方案为 ( 1 ^ ) (\hat1) (1^) ( 2 ^ ) (\hat2) (2^) ( 1 ^ , 2 ) (\hat1,2) (1^,2) ( 1 , 2 ^ ) (1,\hat2) (1,2^),其中 x ^ \hat x x^表示选取 x x x为队长。

今天接到了阿里的面试电话,之后讨论了下发现有更简单的解法,更新一下。(数学太重要了)
更新思路:利用① C n i = C n n − i C^i_n=C^{n-i}_n Cni=Cnni和② ∑ i = 0 n C n i = 2 n \sum^n_{i=0}C^i_n=2^n i=0nCni=2n来化简。推导如下:
f(n) = ∑ n = 1 n i ∗ C n i \sum^n_{n=1}i*C^i_n n=1niCni
2f(n) = ∑ n = 1 n i ∗ C n i \sum^n_{n=1}i*C^i_n n=1niCni + ∑ n = 1 n ( n − i ) ∗ C n i \sum^n_{n=1}(n-i)*C^i_n n=1n(ni)Cni (①)
2f(n) = ∑ n = 1 n i ∗ C n i \sum^n_{n=1}i*C^i_n n=1niCni + ∑ n = 1 n ( n − i ) ∗ C n i \sum^n_{n=1}(n-i)*C^i_n n=1n(ni)Cni + 2*0* C n n C^n_n Cnn
2f(n) = ∑ n = 0 n i ∗ C n i \sum^n_{n=0}i*C^i_n n=0niCni = n 2 n n2^n n2n
f(n)= n 2 n − 1 n2^{n-1} n2n1
代码

# -*- coding:utf-8 -*-
class Solution:
    # f(n) = \sum^n_(n-1) i*C^1_n
    #      = n*2^(n-1)
    def NumOfSquad(self, n):
        if n < 1 or n > 10**9:
            return 0
        return (n*(2**(n-1)))%(10**9+7)

思路
n=1,f(1) = C 1 1 C^1_1 C11*1
n=2,f(2) = C 2 1 C^1_2 C21*1 + C 2 2 C^2_2 C22*2
n=3,f(3) = C 3 1 C^1_3 C31*1 + C 3 2 C^2_3 C32*2 + C 3 3 C^3_3 C33*3
……
其中 C n x C^x_n Cnx = n ! ( n − x ) ! x ! \frac{n!}{(n-x)!x!} (nx)!x!n!

在计算f(n)时,需要计算排列组合,排列组合公式用到了阶乘。如果空间复杂度要求不高,还可以将前面的结果都保存下来,这样可以减少计算量。

代码

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.fact_x = 0
        self.fact_n_x = 0

    def NumOfSquad(self, n):
        if n < 1 or n > 10**9:
            return 0
        sum = 0
        for i in range(1,n+1):
            sum += self.NumOfChoose(n,i) * i
        return sum%(10**9+7)
            
        
    def NumOfChoose(self, n, x):
        if x == 1:
            return n
        return int(self.factorial(n,x)/self.fact_n_x/self.fact_x)

    def factorial(self, n, x):
        if n == 1:
            return 1
        res = 1
        for i in range(1,n+1):
            res =((res%(10**9+7)) * (i%(10**9+7))) % (10**9+7)
            if i == x:
                self.fact_x = res
            if i == n-x:
                self.fact_n_x = res
        return res

if __name__ == "__main__":
    num = 10000
    solution = Solution1()
    
    import time
    start = time.clock()
    
    print(solution.NumOfSquad(num))
    
    end = time.clock()
    print(str(end-start))

输出结果

20000
21.9018636

题目2:对称飞行器

小强在玩一个走迷宫的游戏,他操控的人物现在位于迷宫的起点,他的目标是尽快到达终点。
每一次他可以选择花费一个时间单位向上或向下或向左或向右走一格,或是使用自己的对称飞行器花费一个时间单位瞬移到关于当前自己点中心对称的格子,且每一次移动的目的地不能存在障碍物。
具体来说,设当前迷宫有nm列,如果当前小强操控的人物位于点 A ( x , y ) A(x,y) A(x,y),那么关于点A中心对称的格子 B ( x ′ , y ′ ) B(x',y') B(x,y)满足 x + x ′ = n + 1 x+x'=n+1 x+x=n+1 y + y ′ = m + 1 y+y'=m+1 y+y=m+1
需要注意的是,对称飞行器最多使用5次。
输入描述

第一行两个空格分隔的正整数nm,分别代表迷宫的行数和列数。接下来n行每行一个长度为m的字符串来描述这个迷宫。
其中
⋅ \cdot 代表通路
# \# #代表障碍
S S S代表起点
E E E代表终点
保证只有一个 S S S和一个 E E E
2 < = n , m < = 500 2<=n,m<=500 2<=n,m<=500

输出描述

仅一行一个整数表示从起点最小花费多少时间单位到达终点。如果无法到达终点,输出-1。

示例1
输入

4 4
#S ⋅ \cdot ⋅ \cdot
E# ⋅ \cdot ⋅ \cdot
# ⋅ \cdot ⋅ \cdot ⋅ \cdot
⋅ \cdot ⋅ \cdot ⋅ \cdot ⋅ \cdot

输出

4

说明

一种可行的路径是用对称飞行器到达(4,3)再向上走一步,再向右走一步,然后后使用一次对称飞行器到达终点。

你可能感兴趣的:(刷题笔记)