雙周賽109。還是dp,測資範圍很奇怪,總感覺有奇怪的地雷,害我擔心很久。
雖然我是沒有踩中,但是理論上不重複的測資只有300*5種,官方卻搞了1502組,不知道存什麼心。

另外小抱怨一下,這次周賽Q3和Q4給的參數x和n都卡到我常用變數名稱,python重複宣告又不會出錯,浪費好多時間debug。

題目

輸入兩個正整數n和x。

求將n拆成數個不重複正整數的x次方總和,共有幾種方法。
也就是說,找到某些不同的整數[n1, n2,… ,nk],滿足n = [n1x, n2x,… ,nkx]。

例如n = 160, x = 3,其中一種方法是n = 23+33+53

答案可能很大,先模10^9+7後回傳。

解法

如果x越大,某數a的x次方能組成n機率越小。
最差的情況下x=1,那麼n有可以由1~n的所有數字來組成,那麼先篩出所有可能的數nums。

我們只在乎選了哪些數,不在乎順序。也就是決定nums中的數選或不選,總和正好等於n的方法有幾種。
其實就是經典的01背包問題。

時間複雜度O(n^2)。
空間複雜度O(n^2)。

class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        MOD=10**9+7
        nums=[i**x for i in range(1,n+1) if i**x<=n]
        
        N=len(nums)
        dp=[[0]*(n+1) for _ in range(N+1)]
        dp[0][0]=1
        
        for i in range(1,N+1):
            val=nums[i-1]
            for j in range(n+1):
                dp[i][j]=dp[i-1][j]
                if j>=val:
                    dp[i][j]+=dp[i-1][j-val]
                    dp[i][j]%=MOD
                    
        return dp[N][n]

比賽中我直接寫出了空間優化的方法,還是挺開心的。

時間複雜度O(n^2)。
空間複雜度O(n)。

class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        MOD=10**9+7
        nums=[i**x for i in range(1,n+1) if i**x<=n]
        
        dp=[0]*(n+1)
        dp[0]=1
        for val in nums:
            for j in reversed(range(val,n+1)):
                dp[j]+=dp[j-val]
                dp[j]%=MOD
                
        return dp[n]

但是我就算沒有先預處理可能的數字,用記憶化搜索也沒有TLE。有點好奇被卡的人是什麼情況。

時間複雜度O(n^2)。
空間複雜度O(n^2)。

class Solution:
    def numberOfWays(self, n: int, x: int) -> int:
        MOD=10**9+7
        
        @cache 
        def dp(n,i):
            if n==0:
                return 1
            val=i**x
            if val>n:
                return 0
            res=dp(n-val,i+1)+dp(n,i+1)
            return res%MOD
        
        return dp(n,1)