动态规划 
      
    
      
      
      
        数学 
      
    
      
      
      
        组合数学 
      
    
   
  
    
      
       
     
  
  
    
      
    
    
      
       
     
  
题目描述 
你的音乐播放器里有 n 首不同的歌,在旅途中,你计划听 goal 首歌(不一定不同,即,允许歌曲重复)。你将会按如下规则创建播放列表:
    每首歌 至少播放一次  。 
    一首歌只有在其他 k 首歌播放完之后才能再次播放。 
 
给你 n、goal 和 k ,返回可以满足要求的播放列表的数量。由于答案可能非常大,请返回对 109  + 7 取余  的结果。
 
示例 1: 
输入: n = 3, goal = 3, k = 1
输出: 6
解释: 有 6 种可能的播放列表。[1, 2, 3],[1, 3, 2],[2, 1, 3],[2, 3, 1],[3, 1, 2],[3, 2, 1] 。
 
示例 2: 
输入: n = 2, goal = 3, k = 0
输出: 6
解释: 有 6 种可能的播放列表。[1, 1, 2],[1, 2, 1],[2, 1, 1],[2, 2, 1],[2, 1, 2],[1, 2, 2] 。
 
示例 3: 
输入: n = 2, goal = 3, k = 1
输出: 2
解释: 有 2 种可能的播放列表。[1, 2, 1],[2, 1, 2] 。
 
 
提示: 
    0 <= k < n <= goal <= 100 
 
解法 
方法一:动态规划 
我们定义 \(f[i][j]\)  表示听 \(i\)  首歌,且这 \(i\)  首歌中有 \(j\)  首不同歌曲的播放列表的数量。初始时 \(f[0][0]=1\) 。答案为 \(f[goal][n]\) 。
对于 \(f[i][j]\) ,我们可以选择没听过的歌,那么上一个状态为 \(f[i - 1][j - 1]\) ,这样的选择有 \(n - (j - 1) = n - j + 1\)  种,因此 \(f[i][j] += f[i - 1][j - 1] \times (n - j + 1)\) 。我们也可以选择听过的歌,那么上一个状态为 \(f[i - 1][j]\) ,这样的选择有 \(j - k\)  种,因此 \(f[i][j] += f[i - 1][j] \times (j - k)\) ,其中 \(j \geq k\) 。
综上,我们可以得到状态转移方程:
\[
f[i][j] = \begin{cases}
1 & i = 0, j = 0 \\
f[i - 1][j - 1] \times (n - j + 1) + f[i - 1][j] \times (j - k) & i \geq 1, j \geq 1
\end{cases}
\]
最终的答案为 \(f[goal][n]\) 。
时间复杂度 \(O(goal \times n)\) ,空间复杂度 \(O(goal \times n)\) 。其中 \(goal\)  和 \(n\)  为题目中给定的参数。
Python3 Java C++ Go TypeScript Rust 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 class   Solution : 
    def   numMusicPlaylists ( self ,  n :  int ,  goal :  int ,  k :  int )  ->  int : 
        mod  =  10 ** 9  +  7 
        f  =  [[ 0 ]  *  ( n  +  1 )  for  _  in  range ( goal  +  1 )] 
        f [ 0 ][ 0 ]  =  1 
        for  i  in  range ( 1 ,  goal  +  1 ): 
            for  j  in  range ( 1 ,  n  +  1 ): 
                f [ i ][ j ]  =  f [ i  -  1 ][ j  -  1 ]  *  ( n  -  j  +  1 ) 
                if  j  >  k : 
                    f [ i ][ j ]  +=  f [ i  -  1 ][ j ]  *  ( j  -  k ) 
                f [ i ][ j ]  %=  mod 
        return  f [ goal ][ n ] 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 class  Solution   { 
     public   int   numMusicPlaylists ( int   n ,   int   goal ,   int   k )   { 
         final   int   mod   =   ( int )   1e9   +   7 ; 
         long [][]   f   =   new   long [ goal   +   1 ][ n   +   1 ] ; 
         f [ 0 ][ 0 ]   =   1 ; 
         for   ( int   i   =   1 ;   i   <=   goal ;   ++ i )   { 
             for   ( int   j   =   1 ;   j   <=   n ;   ++ j )   { 
                 f [ i ][ j ]   =   f [ i   -   1 ][ j   -   1 ]   *   ( n   -   j   +   1 ); 
                 if   ( j   >   k )   { 
                     f [ i ][ j ]   +=   f [ i   -   1 ][ j ]   *   ( j   -   k ); 
                 } 
                 f [ i ][ j ]   %=   mod ; 
             } 
         } 
         return   ( int )   f [ goal ][ n ] ; 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 class   Solution   { 
public : 
     int   numMusicPlaylists ( int   n ,   int   goal ,   int   k )   { 
         const   int   mod   =   1e9   +   7 ; 
         long   long   f [ goal   +   1 ][ n   +   1 ]; 
         memset ( f ,   0 ,   sizeof ( f )); 
         f [ 0 ][ 0 ]   =   1 ; 
         for   ( int   i   =   1 ;   i   <=   goal ;   ++ i )   { 
             for   ( int   j   =   1 ;   j   <=   n ;   ++ j )   { 
                 f [ i ][ j ]   =   f [ i   -   1 ][ j   -   1 ]   *   ( n   -   j   +   1 ); 
                 if   ( j   >   k )   { 
                     f [ i ][ j ]   +=   f [ i   -   1 ][ j ]   *   ( j   -   k ); 
                 } 
                 f [ i ][ j ]   %=   mod ; 
             } 
         } 
         return   f [ goal ][ n ]; 
     } 
}; 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 func   numMusicPlaylists ( n   int ,   goal   int ,   k   int )   int   { 
     const   mod   =   1e9   +   7 
     f   :=   make ([][] int ,   goal + 1 ) 
     for   i   :=   range   f   { 
         f [ i ]   =   make ([] int ,   n + 1 ) 
     } 
     f [ 0 ][ 0 ]   =   1 
     for   i   :=   1 ;   i   <=   goal ;   i ++   { 
         for   j   :=   1 ;   j   <=   n ;   j ++   { 
             f [ i ][ j ]   =   f [ i - 1 ][ j - 1 ]   *   ( n   -   j   +   1 ) 
             if   j   >   k   { 
                 f [ i ][ j ]   +=   f [ i - 1 ][ j ]   *   ( j   -   k ) 
             } 
             f [ i ][ j ]   %=   mod 
         } 
     } 
     return   f [ goal ][ n ] 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 function   numMusicPlaylists ( n :   number ,   goal :   number ,   k :   number ) :   number   { 
     const   mod   =   1e9   +   7 ; 
     const   f   =   new   Array ( goal   +   1 ). fill ( 0 ). map (()   =>   new   Array ( n   +   1 ). fill ( 0 )); 
     f [ 0 ][ 0 ]   =   1 ; 
     for   ( let   i   =   1 ;   i   <=   goal ;   ++ i )   { 
         for   ( let   j   =   1 ;   j   <=   n ;   ++ j )   { 
             f [ i ][ j ]   =   f [ i   -   1 ][ j   -   1 ]   *   ( n   -   j   +   1 ); 
             if   ( j   >   k )   { 
                 f [ i ][ j ]   +=   f [ i   -   1 ][ j ]   *   ( j   -   k ); 
             } 
             f [ i ][ j ]   %=   mod ; 
         } 
     } 
     return   f [ goal ][ n ]; 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 impl   Solution   { 
     #[allow(dead_code)] 
     pub   fn   num_music_playlists ( n :   i32 ,   goal :   i32 ,   k :   i32 )   ->   i32   { 
         let   mut   dp :   Vec < Vec < i64 >>   =   vec! [ vec! [ 0 ;   n   as   usize   +   1 ];   goal   as   usize   +   1 ]; 
         // Initialize the dp vector 
         dp [ 0 ][ 0 ]   =   1 ; 
         // Begin the dp process 
         for   i   in   1 ..= goal   as   usize   { 
             for   j   in   1 ..= n   as   usize   { 
                 // Choose the song that has not been chosen before 
                 // We have n - (j - 1) songs to choose 
                 dp [ i ][ j ]   +=   dp [ i   -   1 ][ j   -   1 ]   *   (( n   -   (( j   as   i32 )   -   1 ))   as   i64 ); 
                 // Choose the song that has been chosen before 
                 // We have j - k songs to choose if j > k 
                 if   ( j   as   i32 )   >   k   { 
                     dp [ i ][ j ]   +=   dp [ i   -   1 ][ j ]   *   ((( j   as   i32 )   -   k )   as   i64 ); 
                 } 
                 // Update dp[i][j] 
                 dp [ i ][ j ]   %=   (( 1e9   as   i32 )   +   7 )   as   i64 ; 
             } 
         } 
         dp [ goal   as   usize ][ n   as   usize ]   as   i32 
     } 
} 
 
 
 
 
方法二:动态规划(空间优化) 
我们注意到 \(f[i][j]\)  只与 \(f[i - 1][j - 1]\)  和 \(f[i - 1][j]\)  有关,因此我们可以使用滚动数组优化空间复杂度,将空间复杂度优化至 \(O(n)\) 。
Python3 Java C++ Go TypeScript 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 class   Solution : 
    def   numMusicPlaylists ( self ,  n :  int ,  goal :  int ,  k :  int )  ->  int : 
        mod  =  10 ** 9  +  7 
        f  =  [ 0 ]  *  ( goal  +  1 ) 
        f [ 0 ]  =  1 
        for  i  in  range ( 1 ,  goal  +  1 ): 
            g  =  [ 0 ]  *  ( goal  +  1 ) 
            for  j  in  range ( 1 ,  n  +  1 ): 
                g [ j ]  =  f [ j  -  1 ]  *  ( n  -  j  +  1 ) 
                if  j  >  k : 
                    g [ j ]  +=  f [ j ]  *  ( j  -  k ) 
                g [ j ]  %=  mod 
            f  =  g 
        return  f [ n ] 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 class  Solution   { 
     public   int   numMusicPlaylists ( int   n ,   int   goal ,   int   k )   { 
         final   int   mod   =   ( int )   1e9   +   7 ; 
         long []   f   =   new   long [ n   +   1 ] ; 
         f [ 0 ]   =   1 ; 
         for   ( int   i   =   1 ;   i   <=   goal ;   ++ i )   { 
             long []   g   =   new   long [ n   +   1 ] ; 
             for   ( int   j   =   1 ;   j   <=   n ;   ++ j )   { 
                 g [ j ]   =   f [ j   -   1 ]   *   ( n   -   j   +   1 ); 
                 if   ( j   >   k )   { 
                     g [ j ]   +=   f [ j ]   *   ( j   -   k ); 
                 } 
                 g [ j ]   %=   mod ; 
             } 
             f   =   g ; 
         } 
         return   ( int )   f [ n ] ; 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 class   Solution   { 
public : 
     int   numMusicPlaylists ( int   n ,   int   goal ,   int   k )   { 
         const   int   mod   =   1e9   +   7 ; 
         vector < long   long >   f ( n   +   1 ); 
         f [ 0 ]   =   1 ; 
         for   ( int   i   =   1 ;   i   <=   goal ;   ++ i )   { 
             vector < long   long >   g ( n   +   1 ); 
             for   ( int   j   =   1 ;   j   <=   n ;   ++ j )   { 
                 g [ j ]   =   f [ j   -   1 ]   *   ( n   -   j   +   1 ); 
                 if   ( j   >   k )   { 
                     g [ j ]   +=   f [ j ]   *   ( j   -   k ); 
                 } 
                 g [ j ]   %=   mod ; 
             } 
             f   =   move ( g ); 
         } 
         return   f [ n ]; 
     } 
}; 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 func   numMusicPlaylists ( n   int ,   goal   int ,   k   int )   int   { 
     const   mod   =   1e9   +   7 
     f   :=   make ([] int ,   goal + 1 ) 
     f [ 0 ]   =   1 
     for   i   :=   1 ;   i   <=   goal ;   i ++   { 
         g   :=   make ([] int ,   goal + 1 ) 
         for   j   :=   1 ;   j   <=   n ;   j ++   { 
             g [ j ]   =   f [ j - 1 ]   *   ( n   -   j   +   1 ) 
             if   j   >   k   { 
                 g [ j ]   +=   f [ j ]   *   ( j   -   k ) 
             } 
             g [ j ]   %=   mod 
         } 
         f   =   g 
     } 
     return   f [ n ] 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 function   numMusicPlaylists ( n :   number ,   goal :   number ,   k :   number ) :   number   { 
     const   mod   =   1e9   +   7 ; 
     let   f   =   new   Array ( goal   +   1 ). fill ( 0 ); 
     f [ 0 ]   =   1 ; 
     for   ( let   i   =   1 ;   i   <=   goal ;   ++ i )   { 
         const   g   =   new   Array ( goal   +   1 ). fill ( 0 ); 
         for   ( let   j   =   1 ;   j   <=   n ;   ++ j )   { 
             g [ j ]   =   f [ j   -   1 ]   *   ( n   -   j   +   1 ); 
             if   ( j   >   k )   { 
                 g [ j ]   +=   f [ j ]   *   ( j   -   k ); 
             } 
             g [ j ]   %=   mod ; 
         } 
         f   =   g ; 
     } 
     return   f [ n ]; 
} 
 
 
 
 
  
  
  
    
    
    
    
      
  
    
      
  
     
   
  GitHub