LeetCode279 完全平方数

56 阅读1分钟

leetcode.cn/problems/pe…

image.png

求和为 n 的完全平方数的最小数量,可以根据和为 n-1x1, n-2x2, n-3x3... 的完全平方数的最小数量推导出来。

这个问题完全可以变化成零钱兑换问题:给你一个目标金额 n,和一个若干硬币的面额 coins = 1,4,9,16...,问最少需要几枚硬币凑出这个金额(因为包含面值为 1 的硬币,所以不存在凑不出来的情况)。

解法一:自顶向下的递归DP + 备忘录

func numSquares(n int) int {
    // 把所有小于等于整数 n的完全平方数纳入选择列表
    choices := make([]int, 0)
    for i:=1; i*i<=n; i++{
        choices = append(choices, i*i)
    }
    // 备忘录初始化为-1,不会和可能答案冲突
    memo := make([]int, n+1)
    for idx := range memo{
        memo[idx] = -1
    }
    return dp(choices, n, memo)
}

func dp(choices []int, sum int, memo []int) int{
    if sum == 0{
        return 0
    }
    if memo[sum] != -1{
        return memo[sum]
    }
    res := sum // 为了求min,初始化为最大数量,就是当全选择 1时
    for _, choice := range choices{
        if sum - choice < 0{
            continue
        }
        res = min(res, dp(choices, sum-choice, memo)+1)
    }
    memo[sum] = res
    return memo[sum]
}

func min(a, b int) int{
    if a < b{
        return a
    }
    return b
}

解法二:自底向上的递推DP

func numSquares(n int) int {
    // dp[i]表示和为 i的完全平方数的最小数量
    dp := make([]int, n+1)
    // base case, 和为0不存在完全平方数
    dp[0] = 0
    for i := 1; i<len(dp); i++{
        dp[i] = i // 最差情况是全部选1,总共需要 i个数
        for j := 1; j*j <=i; j++{ // 在比 i小的完全平方数中做选择
            dp[i] = min(dp[i], dp[i-j*j]+1)
        }
    }
    return dp[n]
}

func min(a, b int) int{
    if a < b{
        return a
    }
    return b
}