leetcode_day6_筑基期_《绝境求生》

17 阅读5分钟

目录


前言

碎碎念:灵魂的欲望是命运的先知

本系列《绝境求生》记录转码算法筑基过程,以代码随想录为纲学习,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、优化法

拉格朗日四平方和定理

  1. 拉格朗日四平方和定理:每个自然数都可以表示为至多 4 个完全平方数的和。

  2. 推论

    • 若 n = 4^k * (8m + 7)k,m 为非负整数),则 n 必须用4 个完全平方数表示;
    • 否则,检查是否能表示为1 个n 本身是完全平方数)→ 能则返回 1;
    • 若不能,检查是否能表示为2 个(枚举 ,判断 n-j² 是否是完全平方数)→ 能则返回 2;
    • 若都不行,返回3 个

 4.1、能不能用图示意?

1. 检查是否满足4^k*(8m+7):
   12 ÷4=3 → k=1,剩余338m+7 → 不满足,排除4个的情况。
2. 检查是否是1个:√12≈3.464,不是整数 → 排除1个。
3. 检查是否是2个:
   枚举j=112-1=11(不是平方数);
   j=212-4=8(不是平方数);
   j=312-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