目录
前言
碎碎念:灵魂的欲望是命运的先知
本系列《绝境求生》记录转码算法筑基过程,以代码随想录为纲学习,leetcode_hot_100练手,在此记录思考过程,方便过后复现。内容比较粗糙仅便于笔者厘清思路,复盘总结。
此篇后,改变了文章结构
提示:以下是本篇文章正文内容
动态规划
特点:运筹学上最优化算法,题型是求最值,重叠子问题,最优子结构,!!状态转移方程
把复杂问题拆解成小问题,再由小问题的答案推导出大问题的答案(即记住之前做过的事情,避免重复计算,提高效率)
状态定义:用某个变量或数组代表某个小问题的答案
状态转移方程:大问题的答案=小问题答案的组合的数学表达式
初始化条件:最小的问题,不用推导就知道答案
一、279完全平方数
1、题目描述
给定一个正整数
n,找到若干个完全平方数(比如1, 4, 9, 16, ...)的和,使得它们的和等于n。要求组成和的完全平方数的个数最少,返回这个最少的个数。示例
示例 1:输入
n = 12→ 输出3
- 解释:
12 = 4 + 4 + 4(3 个完全平方数,是最少个数;若用 1 则需要 12 个,用 9+1+1+1 需要 4 个)。示例 2:输入
n = 13→ 输出2
- 解释:
13 = 4 + 9(2 个完全平方数,是最少个数)。提示
1 <= n <= 10^4
2、简单理解?
用最少个完全平方数的和组成target,(可以使用重复的完全平方数)
3、暴力法
3.1、能不能用图示意?
# 初始化dp数组(长度13,索引0~12) dp[0] = 0 dp[1] = dp[1-1²] +1 = dp[0]+1=1 dp[2] = dp[2-1²] +1 = dp[1]+1=2 dp[3] = dp[3-1²] +1 = dp[2]+1=3 dp[4] = min(dp[4-1²]+1, dp[4-2²]+1) = min(dp[3]+1=4, dp[0]+1=1) → 1 dp[5] = min(dp[5-1²]+1, dp[5-2²]+1) = min(dp[4]+1=2, dp[1]+1=2) → 2 dp[6] = min(dp[6-1²]+1, dp[6-2²]+1) = min(dp[5]+1=3, dp[2]+1=3) → 3 dp[7] = min(dp[7-1²]+1, dp[7-2²]+1) = min(dp[6]+1=4, dp[3]+1=4) → 4 dp[8] = min(dp[8-1²]+1, dp[8-2²]+1) = min(dp[7]+1=5, dp[4]+1=2) → 2 dp[9] = min(dp[9-1²]+1, dp[9-2²]+1, dp[9-3²]+1) = min(5,3,1) →1 dp[10] = min(dp[10-1²]+1, dp[10-2²]+1, dp[10-3²]+1) = min(2,3,2) →2 dp[11] = min(dp[11-1²]+1, dp[11-2²]+1, dp[11-3²]+1) = min(3,4,3) →3 dp[12] = min(dp[12-1²]+1, dp[12-2²]+1, dp[12-3²]+1) = min(dp[11]+1=4, dp[8]+1=3, dp[3]+1=4) →3
3.2、初始化条件?
dp[0]=0, dp[1]=1
3.3、边界条件?
剪枝 target=0 or 1 的情况
3.4、代码逻辑?
状态定义dp[i ]表示 组成正整数i的最少完全平方个数
dp数组下标代表target。 对应的值代表实现平方和target的最少数
对于i,遍历所有不大于i的完全平方数j^2,dp[i]=min(dp[i-j^2]+1) (+1 是加上当前选的 j^2)
外层循环,从1-n 逐个计算每个数的最少个数
内层循环,遍历所有不大于 i 的完全平方数,避免无效运算
3.5、之前见过但没注意到的?
5的平方不能直接用5^2表示。最好是square=5*5
!!! dp = [float('inf')] * (n + 1):初始值设为极大值,因为一开始不知道每个 i 的最少个数,后续通过转移方程更新;
3.6、疑惑点/新知识 ?
搞清楚索引和数组长度分别定义什么?
3.7、python 代码
class Solution:
def numSquares(self, target: int) -> int:
if target==0:
return 0
if target==1:
return 1
import math
#初始化
dp=[float('inf')]*(target+1)
dp[0]=0
dp[1]=1
for i in range(2,target+1):
max_j=int(math.sqrt(target))
for j in range(1,max_j+1):
square=j*j
dp[i] = min(dp[i], dp[i-square] + 1)
return dp[target]
#定义方程
4、优化法
拉格朗日四平方和定理
-
拉格朗日四平方和定理:每个自然数都可以表示为至多 4 个完全平方数的和。
-
推论:
- 若
n = 4^k * (8m + 7)(k,m为非负整数),则n必须用4 个完全平方数表示; - 否则,检查是否能表示为1 个(
n本身是完全平方数)→ 能则返回 1; - 若不能,检查是否能表示为2 个(枚举
j²,判断n-j²是否是完全平方数)→ 能则返回 2; - 若都不行,返回3 个。
- 若
4.1、能不能用图示意?
1. 检查是否满足4^k*(8m+7): 12 ÷4=3 → k=1,剩余3;3≠8m+7 → 不满足,排除4个的情况。 2. 检查是否是1个:√12≈3.464,不是整数 → 排除1个。 3. 检查是否是2个: 枚举j=1:12-1=11(不是平方数); j=2:12-4=8(不是平方数); j=3:12-9=3(不是平方数); → 排除2个。 4. 最终返回3个。
4.2、初始化条件?
none
4.3、边界条件?
none
4.4、代码逻辑?
写一个子函数 判断一个数是否为完全平方数
检查是否满足 target= 4k*(8m+7)是return 4个 # 这里用到了while 连除,注意一下循环条件
一般都是先对倍数取余
判断target-j^2是否为完全平方数,是return 2 个
以上都不满足返回3个
4.5、之前见过但没注意到的?
判断是否为完全平方数的函数怎么写。 是就返回自己,不是就返回离自己最近的完全平方数
4.6、疑惑点/新知识 ?
取余本身就是一个连除的操作
4.7、python 代码
class Solution:
def numSquares(self, target: int) -> int:
def is_square(n):
sqrt_n = int(math.sqrt(n))
return sqrt_n * sqrt_n
temp=target
#判断是否为4种
while temp % 4==0:
temp//= 4
if temp % 8==7:
return 4
#判断是否为1种,就是自己本身就是完全平方数
if target==is_square(target):
return 1
#判断是否为2种
for i in range(1,target+1):
max_j=int(math.sqrt(target))
for j in range(1,max_j+1):
square=j*j
if target-square==is_square(target-square):
return 2
return 3