完全平方數
給定正整數 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
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/perfect-squares
解題思路演化過程
第一次思路
首先對n求平方根結果為a,如果aa=n,則將a自減1,然后再n-(a-1)(a-1)。將得到的結果循環,但是不要再判斷是否aa=n這種情況。每次執行都要sum加一。當n=1或者n=0時,跳出循環。如果最后n=1則sum+1,然后返回結果。
但是這樣錯誤的,n=12的時候就會變成:9+3這樣就會比4+4+4的多。
代碼
func numSquares(n int) int {
sum := 0
one := 0
for n!=0&&n!=1 {
// 這樣還是不對,當n=12的時候就會變成:9+3這樣就會比4+4+4的多
sqrtVal := int(math.Sqrt(float64(n)))
if sqrtVal*sqrtVal==n&&one==0{
sqrtVal--
one++
}
sum++
n = n - sqrtVal*sqrtVal
}
if n==1 {
sum++
}
return sum
}
第二次思路
靠靠靠,這道題感覺是有毛病的,它說是之和,我認為就是是兩個數以上才是之和,誰知道一個數也是之和,靠靠靠。如果是這樣的話,完全就是一個01背包問題嘛。和之前作過的三數之和是類似的思路。
根據枚舉發現,組合數中各個數一定是在[1,n的平方根]之間。我們可以從1開始枚舉這些數,求出每次枚舉對應[0,n]之間各個數最少需要多少個數的平方來表示整數 。並且遵循:n = n - i*i的規律進行循環計算。
因此可以假設dp[i][j]表示在1到i之間最少需要多少個數的平方來表示整數j。i的取值范圍:[1,n的平方根],j的取值范圍:[0,n]。對於ji*i的情況,dp[i][j]1。對於j<i*i的情況,我們只能取dp[i][j]=dp[i-1][j]。對於j>i*i的情況,我們可以不取也可以取,我們只要取其中的最小值。對於取:dp[i][j]=j/(i*i) + dp[i][j%i*i]。
代碼
func numSquares(n int) int {
sqrtVal := int(math.Sqrt(float64(n)))
dp := make([][]int,sqrtVal+1)
for i:=0;i<len(dp);i++{
dp[i] = make([]int,n+1)
}
// 初始化邊界
for i:=1;i<n+1;i++{
dp[1][i]=1
}
for i:=1;i<sqrtVal+1;i++{
if i>1 {
dp[i][1] = 1
}
}
// 第二次的時候,就不對啊
for i:=2;i<sqrtVal+1;i++{
for j:=2;j<n+1;j++{
// 如果小或等於,不取
if j<i*i {
dp[i][j] = dp[i-1][j]
}else if j>i*i {
// 如果大於
// 取,以及不取
tmp := j/(i*i) + dp[i][j%(i*i)]
dp[i][j] = min(tmp,dp[i-1][j])
}else if j==i*i {
dp[i][j] = 1
}
}
}
return dp[sqrtVal][n]
}
func min(x,y int)int{
if x<y {
return x
}
return y
}
超級強大的定律:四平方和定理證明了任意一個正整數都可以被表示為至多四個正整數的平方和。這給出了本題的答案的上界。
同時四平方和定理包含了一個更強的結論:當且僅n!=4k*(8m+7)當時,可以被表示為至多三個正整數的平方和。因此,當時n=4k*(8m+7),n只能被表示為四個正整數的平方和。此時我們可以直接返回 4。
對於不等的情況,我們可判斷:
- 如果n是一個平方數的時候,直接等於1
- 如果n符合a^2 + b^2= n的時候,直接等於2
- 剩下的就是直接返回3。