279. Perfect Squares(完全平方數)
阿新 • • 發佈:2020-11-17
-
Difficulty: Medium
-
Related Topics: Math, Dynamic Programming, Breadth-first Search
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()
}
}