• [LeetCode 920] Number of Music Playlists


    Your music player contains N different songs and she wants to listen to L (not necessarily different) songs during your trip.  You create a playlist so that:

    • Every song is played at least once
    • A song can only be played again only if K other songs have been played

    Return the number of possible playlists.  As the answer can be very large, return it modulo 10^9 + 7.

    Example 1:

    Input: N = 3, L = 3, K = 1
    Output: 6
    Explanation: There are 6 possible playlists. [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1].
    

    Example 2:

    Input: N = 2, L = 3, K = 0
    Output: 6
    Explanation: There are 6 possible playlists. [1, 1, 2], [1, 2, 1], [2, 1, 1], [2, 2, 1], [2, 1, 2], [1, 2, 2]
    

    Example 3:

    Input: N = 2, L = 3, K = 1
    Output: 2
    Explanation: There are 2 possible playlists. [1, 2, 1], [2, 1, 2]
    

    Note:

    1. 0 <= K < N <= L <= 100

    A brute force solution is to check all possible permutations and add those that meet the given requirements to the final count. The runtime is O(L!) * O(L), which is infeasible for a input size up to 100. The following table can be used to guess the required time complexity of the algorithm that solves a problem, assuming a time limit of one second.

    Input size  required time complexity
    n <= 10 O(n !)
    n <= 20 O(2 ^ n)
    n <= 500 O(n ^ 3)
    n <= 5000 O(n ^ 2)
    n <= 10^6 O(nlogn) or O(n)
    n is large O(1) or O(logn)
      

    Anyway, back to business.  If we consider only one song's choice at a time, we can solve this problem recursively. For the last picked song, there can only be two cases. Either it is not picked before or it has been picked before. 

    The recursive formula is as following. If the last song is never picked before, the original problem F(N, L, K) is reduced to F(N - 1, L - 1, K) and we have N songs to choose from. Or the last song has been picked before, the original problem is reduced to F(N, L - 1, K) and we have N - K songs to choose from.(This satisfies the K buffer requirement)

    F(N, L, K) = F(N - 1, L - 1) * N + F(N, L - 1, K) * (N - K)

    There are two base cases:

    1. N == K + 1, this means the number of available songs just meet the buffer requirement. There are N ! permutations and for every permutation, the rest of song choices is fixed since any other ordering breaks the buffering rule.

    2. N == L, since each song must be picked at least once,  there are N ! possible orderings.

    The recursion tree reveals that there will be a lot of redundant recursive computations. For instance, F(N - 1, L - 2) will be computed twice. This is an indication of dynamic programming. A good trick of computing factorials is to use a 1D arrray to store all factorials. This avoids redundant computations as well.

    Both dp solutions have a runtime of O((N - K) * (L - K)) and space of O(N * L).

    Top down recursion with memoization 

    class Solution {  
        private int mod = (int)1e9 + 7;
        private long[] factorial;
        private long[][] dp;
        public int numMusicPlaylists(int N, int L, int K) {     
            factorial = new long[N + 1];
            dp = new long[N + 1][L + 1];
            factorial[0] = 1;
            for(int i = 1; i <= N; i++) {
                factorial[i] = factorial[i - 1] * i % mod;
            }
                  
            return (int)recursionWithMemo(N, L, K);
        }
        private long recursionWithMemo(int n, int l, int k) {
            if(dp[n][l] == 0) {
                if(n == l || n == k + 1) {
                    dp[n][l] = factorial[n];
                }
                else {
                    dp[n][l] = (recursionWithMemo(n - 1, l - 1, k) * n + recursionWithMemo(n, l - 1, k) * (n - k)) % mod;
                }
            }
            return dp[n][l];
        }
    }

    Bottom up DP

    class Solution {
        public int numMusicPlaylists(int N, int L, int K) {
            int mod = (int)1e9 + 7;
            long[] factorial = new long[N + 1];
            factorial[0] = 1;
            for(int i = 1; i <= N; i++) {
                factorial[i] = factorial[i - 1] * i % mod;
            }
            long[][] dp = new long[N + 1][L + 1];
            for(int i = K + 1; i <= N; i++) {
                for(int j = i; j <= L; j++) {
                    if(i == j || i == K + 1) {
                        dp[i][j] = factorial[i];
                    }
                    else {
                        dp[i][j] = (dp[i - 1][j - 1] * i + dp[i][j - 1] * (i - K)) % mod;
                    }
                }
            }
            return (int)dp[N][L];
        }
    }
  • 相关阅读:
    F#周报2019年第40期
    F#周报2019年第39期
    F#周报2019年第38期
    python模块之time_random
    python小趣味_520绘制一个心形.
    python模块之json_pickle_shelve
    python基础17_列表推导式 vs 生成器表达式
    python基础16_闭包_装饰器
    Spark安装笔记
    python基础15下_迭代器_生成器
  • 原文地址:https://www.cnblogs.com/lz87/p/11651570.html
Copyright © 2020-2023  润新知