zoukankan      html  css  js  c++  java
  • [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];
        }
    }
  • 相关阅读:
    关于For循环的性能
    CLR读书笔记
    轻量级自动化测试框架介绍
    loadrunner中如何将MD5加密的值转换为大写
    LoadRunner 中实现MD5加密
    新安装的soapui启动时报错及解决方法
    单元测试之驱动模块和桩模块的作用和区别
    接口自动化(Python)-利用正则表达式从返回的HTML文本中截取自己想要的值
    LoadRunner性能测试-loadrunner事务
    LoadRunner性能测试-loadrunner工具破解
  • 原文地址:https://www.cnblogs.com/lz87/p/11651570.html
Copyright © 2011-2022 走看看