LeetCode-279 完全平方數
阿新 • • 發佈:2021-12-21
題目來源
題目詳情
給定正整數n,找到若干個完全平方數(比如1, 4, 9, 16, ...
)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。
給你一個整數 n
,返回和為 n
的完全平方數的 最少數量 。
完全平方數 是一個整數,其值等於另一個整數的平方;換句話說,其值等於一個整數自乘的積。例如,1
、4
、9
和 16
都是完全平方數,而 3
和 11
不是。
示例1:
輸入: n = 12
輸出: 3
解釋: 12 = 4 + 4 + 4
示例 2:
輸入: n = 13
輸出: 2
解釋: 13 = 4 + 9
提示:
1 <= n <= 104
題解分析
完全揹包問題變形-二維陣列解法
- 從題目可以分析出,這題其實是完全揹包問題的變形,整個題目都能看到完全揹包問題的影子。
- 題目中的n就可以類比於揹包的容量,這些計算出來的完全平方數就相當於商品,可以假設每個完全平方數的價值為1(相當於把個數看成價值)。
- 由此,可以構造出狀態轉移陣列,假設\(dp[i][j]\)表示前i個數中,可以拼湊出數字和恰好是j的最小數字個數。進而,根據定義,可以定義狀態轉移函式:\(dp[i][j] = Math.min(dp[i-1][j], dp[i][j-nums[i]] + 1)\)。
- 需要注意的是,這裡的狀態轉移方程中的第二個條件,dp[i][j-1]是與01揹包不同的地方,因為這些平方數可以選擇無限個,其實\(dp[i][j-nums[i]] + 1\)
- 最後,我們還得考慮一下邊界值。\(dp[i][0]\)可以表示為前i個數中選出若干個數的和為0的最小個數,這其實可以賦值為0,因為不可能拼湊出0,因為最小的平方數都是1。而\(dp[0][j]\)則可以理解為用0個數拼湊出j是絕不可能的,所以賦值為最大值0X3F3F3F3F。
class Solution { final int MAXS = 0X3F3F3F3F; public int numSquares(int n) { int sn = (int)Math.sqrt(n); int[] nums = new int[sn+1]; for(int i=0; i<=sn; i++){ nums[i] = i * i; } // dp[i][j] = min(dp[i-1][j], dp[i][j-nums[i]]) int[][] dp = new int[sn+1][n+1]; for(int i =0; i<=sn; i++){ Arrays.fill(dp[i], MAXS); dp[i][0] = 0; } for(int i=1; i<=sn; i++){ for(int j=1; j<=n; j++){ if(j >= nums[i]){ dp[i][j] = Math.min(dp[i-1][j], dp[i][j-nums[i]] + 1); }else{ dp[i][j] = dp[i-1][j]; } } } return dp[sn][n]; } }
完全揹包優化-一維陣列解法
- 學過揹包問題的可能都有所瞭解,揹包問題一般都可以進行陣列維度的優化。
- 從我們的狀態轉移方程(\(dp[i][j] = Math.min(dp[i-1][j], dp[i][j-nums[i]] + 1)\))也可以看到,當前狀態只依賴於上一層的狀態以及本輪之前已經計算過的狀態。所以,可以將dp退化為一維陣列,然後採用正序遍歷j的方法,這麼做的原因是需要取到本輪迭代中前面計算的值\(dp[i][j-nums[i]]\)。
- 最後,也要考慮一下邊界值的計算,這裡只有\(dp[0]\)可以被賦值為0,其他位置(類似於解法一中的\(dp[0][j]\))都被賦值為無窮大:0X3F3F3F3F。
- 程式碼如下:
class Solution {
final int MAXS = 0X3F3F3F3F;
public int numSquares(int n) {
int sn = (int)Math.sqrt(n);
int[] nums = new int[sn+1];
for(int i=0; i<=sn; i++){
nums[i] = i * i;
}
// dp[i][j] = min(dp[i-1][j], dp[i][j-nums[i]])
int[] dp = new int[n+1];
Arrays.fill(dp, MAXS);
dp[0] = 0;
for(int i=1; i<=sn; i++){
for(int j=nums[i]; j<=n; j++){
dp[j] = Math.min(dp[j], dp[j-nums[i]] + 1);
}
}
return dp[n];
}
}
Either Excellent or Rusty