Loading

[LeetCode] 279. Perfect Squares(完全平方数)

Description

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
给定一个正整数 n,现需要用完全平方数(1, 4, 9, 16)的和凑出 n,问所需平方数的最小值。

Examples

Example 1

Input: n = 12
Output: 3 
Explanation: 12 = 4 + 4 + 4.

Example 2

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

Solution

先给出一个 naive 的做法,暴力搜索,大致基于下式进行计算(将题目所求的函数记为 \(f(n)\)):

\[f(n) = \begin{cases} 1& n 是完全平方数\\ \min(f(i) + f(n - i)) \; \{ i \in [1, n / 2] \} & else \end{cases} \]

为了防止重复计算,还是采用了记忆化搜索的方法,结果华丽丽的 TLE 了(input = 6175),而我本地跑这组测试数据的时候直接爆栈了……

import kotlin.math.floor
import kotlin.math.min
import kotlin.math.sqrt

class Solution {
    private val memo = hashMapOf<Int, Int>()

    fun numSquares(n: Int): Int {
        if (memo.containsKey(n)) {
            return memo.getValue(n)
        }
        if (n.isPerfectSquare()) {
            memo[n] = 1
            return 1
        }
        var result = Int.MAX_VALUE
        for (i in 1..(n / 2)) {
            result = min(result, numSquares(i) + numSquares(n - i))
        }
        memo[n] = result
        return result
    }

    private fun Int.isPerfectSquare(): Boolean {
        val root = floor(sqrt(this.toDouble())).toInt()
        return root * root == this
    }
}

自然只能找其它解法了,以下一个解法来自 discussion。考虑以下等式(还是一样,将原函数记为 \(f(n)\)):

\[f(0) = 0 \\ f(1) = f(0) + 1 = 1 \\ f(2) = f(1) + 1 = 2 \\ f(3) = f(2) + 1 = 3 \\ f(4) = \min(f(4 - 1 \times 1) + 1, f(4 - 2 \times 2) + 1) = \min(f(3) + 1, f(0) + 1) = 1 \\ f(5) = \min(f(5 - 1 \times 1) + 1, f(5 - 2 \times 2) + 1) = \min(f(4) + 1, f(1) + 1) = 2 \\ ... \\ f(13) = \min(f(13 - 1 \times 1) + 1, f(13 - 2 \times 2) + 1, f(13 - 3 \times 3) + 1) = \min(f(12) + 1, f(9) + 1, f(4) + 1) = 2 \]

所以最后的状态转移方程是:

\[f(n) = \min(f(n - i \times i) + 1)(其中 n - i \times i \geq 0 且 i \geq 1) \]

代码如下:

import kotlin.math.min

class Solution {
    fun numSquares(n: Int): Int {
        val dp = IntArray(n + 1) { Int.MAX_VALUE }
        dp[0] = 0
        for (i in 1..n) {
            var min = Int.MAX_VALUE
            var j = 1
            while (i - j * j >= 0) {
                min = min(min, dp[i - j * j] + 1)
                j++
            }
            dp[i] = min
        }
        return dp.last()
    }
}
posted @ 2020-11-17 09:56  Zhongju.copy()  阅读(71)  评论(0编辑  收藏  举报