图书馆大门前有 n n n 级台阶, 你每次跨上 1 1 1 级或者 2 2 2 级, 请问等上 n n n 级台阶总共有多少种不同的方法? 设计一个算法求解上述问题, 尝试写出公式, 说明算法设计思想和时间复杂度.
算法设计:核心思路是函数的递归调用,当处理 n n n级台阶时,如果跨上1级则还需要处理 n − 1 n-1 n−1级台阶,如果跨上 2 2 2级则还需要处理 n − 2 n-2 n−2级台阶,容易得到递推表达式 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2),这里要注意递归终止条件的设定,为了避免多统计要在 n < 0 n<0 n<0时返回 0 0 0,而在 n = 0 n=0 n=0时返回 1 1 1,代表 1 1 1种走法。
数据输入:台阶级数 n n n
结果输出:走台阶的不同方法数 s o l v e N u m b e r solveNumber solveNumber
伪码描述:
f i n d s o l v e N u m b e r ( n ) : findsolveNumber\ (n): findsolveNumber (n):
递归方程:
{ W ( n ) = W ( n − 1 ) + W ( n − 2 ) W ( 1 ) = 1 \left\{\begin{array}{l} W(n)=W\left(n-1\right)+W\left(n-2\right) \\ W(1)=1 \end{array}\right. {W(n)=W(n−1)+W(n−2)W(1)=1
复杂度分析:由递归方程可知,该算法的时间复杂度相当于斐波那契数列的时间复杂度
W ( n ) = 5 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] = O ( 2 n ) \begin{aligned} W(n)=\frac{\sqrt{5}}{5}\left[\left(\frac{1+\sqrt{5}}{2}\right)^{n}-\left(\frac{1-\sqrt{5}}{2}\right)^{n}\right]=O(2^n) \end{aligned} W(n)=55[(21+5)n−(21−5)n]=O(2n)
该算法除了输入和输出外不需要额外的存储空间,因此空间复杂度是
S ( n ) = O ( 1 ) \begin{aligned} S(n)=O(1 ) \end{aligned} S(n)=O(1)
n n n 种币值 v 1 , v 2 , . . . , v n v_1, v_2, . . . , v_n v1,v2,...,vn 和总钱数 M M M 都是正整数. 如果每种币值的钱币至多使用 1 1 1 次, 问: 对于 M M M 是否可以有一种找零钱的方法? 设计一个算法求解上述问题. 说明算法设计思想, 分析算法最坏情况下的时间复杂度.
算法设计:核心思路是采用动态规划,不断尝试并缩小问题的规模,同时考虑尽可能多的情况,可以设函数 F ( n , M ) F(n,M) F(n,M),即使用前 n n n种钱币是否可以凑出 M M M币值,其值域为 { 0 , 1 } \{0,1\} {0,1}, 1 1 1代表存在至少一种找零钱的方法, 0 0 0代表不存在任何找零钱的方法。
递推方程:
F ( n , M ) = max { F ( n − 1 , M ) , F ( n − 1 , ( M − v n ) } F ( 1 , M ) = { 1 M = v 1 0 M ≠ v 1 F ( n , M ) = 0 M < 0 \begin{array}{l} F(n,M)=\max \left\{F(n-1,M), F(n-1,\left(M-v_{n}\right)\right\}\\ F(1,M)=\left\{\begin{array}{ll} 1 & M=v_{1} \\ 0 & M≠v_{1} \end{array}\right. \\ F(n,M)=0 \quad M<0 \end{array} F(n,M)=max{F(n−1,M),F(n−1,(M−vn)}F(1,M)={10M=v1M=v1F(n,M)=0M<0
数据输入: n n n种币值 v [ 1.. n ] = v 1 , v 2 , . . . , v n v[1..n]=v_1, v_2, . . . , v_n v[1..n]=v1,v2,...,vn和总钱数 M M M
结果输出: 1 1 1或 0 0 0,其中 1 1 1代表存在至少一种找零钱的方法, 0 0 0代表不存在任何找零钱的方法。
伪码描述:
f i n d C h a n g e ( n , M , v ) : findChange\ (n,M,v): findChange (n,M,v):
复杂度分析:由递推方程可知,该算法的时间复杂度为
W ( n ) = O ( n M ) \begin{aligned} W(n)=O(nM) \end{aligned} W(n)=O(nM)
该算法由于通过二进制或运算实现了 M A X MAX MAX函数,因此除了输入和输出外不需要额外的存储空间,因此空间复杂度是
S ( n ) = O ( 1 ) \begin{aligned} S(n)=O(1 ) \end{aligned} S(n)=O(1)
设 P P P 是一台高性能服务器, T = 1 , 2 , . . . , n T = {1, 2, . . . , n} T=1,2,...,n 是 n n n 个计算任务集合, a i a_i ai 表示任务 i i i 所申请的计算资源. 已知服务器的最大计算资源是正整数 K K K. 请确定 T T T 的一个子集 S S S, 使得 ∑ i ∈ S a i ≤ K ∑ _{i∈S} a_i ≤ K ∑i∈Sai≤K, 且 K − ∑ i ∈ S a i K − ∑ _{i∈S} a_i K−∑i∈Sai 的值达到最小. 请设计一个算法求解 S S S, 并分析最坏情况下的时间复杂度.
算法设计:由题目所给的信息可以知道这实质上是一个 0 − 1 0-1 0−1背包问题,物品的价值和重量相等,由于这个背包问题并没有什么特殊已知条件,因此贪心算法并一定可以得到全局最优解,所以最好选用动态规划法来解决。
解决该问题的函数为 F ( n , K ) F(n,K) F(n,K),即只允许前 n n n个任务的申请,总计算资源不超过 K K K时能达到的最大计算资源,使 ∑ i ∈ S a i ∑ _{i∈S} a_i ∑i∈Sai的值达到最大。
递推方程:
F ( n , K ) = max { F ( n − 1 , K ) , F ( n − 1 , K − a n ) + a n } F ( 0 , K ) = 0 F ( n , 0 ) = 0 F ( 1 , K ) = { a 1 , a 1 ≤ K 0 , a 1 > K \begin{array}{l} F(n,K)=\max \left\{F(n-1,K), F\left(n-1,K-a_{n}\right)+a_{n}\right\} \\ F(0,K)=0 \\ F(n,0)=0 \\ F(1,K)=\left\{\begin{array}{ll} a_{1}, & a_{1} \leq K \\ 0, & a_{1}>K \end{array}\right. \\ \end{array} F(n,K)=max{F(n−1,K),F(n−1,K−an)+an}F(0,K)=0F(n,0)=0F(1,K)={a1,0,a1≤Ka1>K
数据输入: n n n 个计算任务的集合 T [ 1.. n ] = { a 1 , a 2 , . . . , a n } T[1..n]=\{a_1,a_2,...,a_n\} T[1..n]={a1,a2,...,an}、服务器最大计算资源K
结果输出:布尔类型数组 S [ 1.. n ] S[1..n] S[1..n],值为 T r u e True True代表相应的任务位于 T T T的子集 S S S中,反之 F a l s e False False为该任务不在 T T T的子集 S S S中。
伪码描述:
f i n d S u b s e t ( n , K ) : findSubset\ (n,K): findSubset (n,K):
复杂度分析:由递推方程可知,该算法的时间复杂度为
W ( n ) = O ( n K ) \begin{aligned} W(n)=O(nK) \end{aligned} W(n)=O(nK)
算法需要一个布尔类型数组 S [ 1.. n ] S[1..n] S[1..n]来记录相应的任务是否位于 T T T的子集 S S S中,另外还有两个变量 x , y x,y x,y用于比较出最优情况,因此空间复杂度是
S ( n ) = O ( n + 2 ) = O ( n ) \begin{aligned} S(n)=O(n+2 )=O(n ) \end{aligned} S(n)=O(n+2)=O(n)
设 I I I 是一个 n n n 位十进制整数. 如果将 I I I 划分为 k k k 段, 则可得到 k k k 个整数. 这 k k k 个整数的乘积称为 I I I 的一个 k k k 乘积. 试设计一个算法, 对于给定的 I I I 和 k k k, 求出 I I I 的最大 k k k 乘积. 尝试写出公式, 并说明算法设计思想和时间复杂度.
算法设计:核心思路依旧是采用动态规划,不断尝试并缩小问题的规模,同时考虑尽可能多的情况,设 n n n位十进制整数 I I I为 I 1 I 2 I 3 . . . I n I_1I_2I_3...I_n I1I2I3...In,函数 p a r t i t i o n ( I , i , j ) partition(I,i,j) partition(I,i,j)可以将 n n n位十进制整数 I I I中第 i i i位到第 j j j位拆分出来成为一个新的十进制整数。
设求解最大 k k k乘积的函数 F ( n , k ) F(n,k) F(n,k)将第 n n n位之前的部分划分为 k k k段得到的最大 k k k乘积,另外还需要找到这个函数的递推方程。
递推方程:
F ( n , k ) = { 0 n < k I 1 I 2 I 3 . . . I n k = 1 max 1 ≤ i < n − 1 { F ( i , k − 1 ) ∗ I i + 1 I i + 2 . . . I n } k > 1 a n d k ≤ n F(n, k)=\left\{\begin{array}{ll} 0 & n
数据输入: n n n位十进制整数 I I I,划分段数 k k k
结果输出: I I I的最大 k k k乘积
伪码描述:
f i n d M a x P r o d u c t ( n , k ) : findMaxProduct\ (n,k): findMaxProduct (n,k):
复杂度分析:由递推方程可知,每层递归的时间复杂度是 O ( 1 + 2 + . . . + n − 1 ) O(1+2+...+n-1) O(1+2+...+n−1),递归深度为 O ( k − 1 ) O(k-1) O(k−1),因此该算法的时间复杂度为
W ( n ) = O ( ( k − 1 ) ( 1 + 2 + . . . + n − 1 ) ) = O ( k n 2 ) \begin{aligned} W(n)=O((k-1)(1+2+...+n-1))=O(kn^2) \end{aligned} W(n)=O((k−1)(1+2+...+n−1))=O(kn2)
该算法每次递归都需要一个额外的 P r o d u c t Product Product数组来记录遍历过程中求出的子乘积,以便于最后使用 M A X MAX MAX函数求出最大 k k k乘积,因此空间复杂度是
S ( n ) = O ( n ) \begin{aligned} S(n)=O(n) \end{aligned} S(n)=O(n)