代码随想录day39|爬楼梯(背包版)322零钱兑换279完全平方数|01笔记

79 阅读1分钟
  • 爬楼梯(背包版)

  • programmercarl.com/0070.%E7%88…
  • 322零钱兑换

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 每种硬币可以取的数目是无限的,属于完全背包问题。最少硬币数量的拿取方式什么顺序都可以,有无顺序都可以得到某种最小值。那么内外不重要。
  • 讲解观后感

  • 确定dp数组以及下标的含义
    dp[j]:凑足总额为j所需钱币的最少个数为dp[j]
  • 确定递推公式
    dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
  • dp[0] = 0;考虑到递推公式的特性,dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中被初始值覆盖。所以下标非0的元素都是应该是最大值。
  • 遍历顺序不重要,因为不管是组合还是排列,都会出现能取到的最小值。
  • 解题代码

  •     // 版本一, 先遍历物品,再遍历背包
        func coinChange1(coins []int, amount int) int {
        	dp := make([]int, amount+1)
        	// 初始化dp[0]
        	dp[0] = 0
        	// 初始化为math.MaxInt32
        	for j := 1; j <= amount; j++ {
        		dp[j] = math.MaxInt32
        	}
        
        	// 遍历物品
        	for i := 0; i < len(coins); i++ {
        		// 遍历背包
        		for j := coins[i]; j <= amount; j++ {
        			if dp[j-coins[i]] != math.MaxInt32 {
        				// 推导公式
        				dp[j] = min(dp[j], dp[j-coins[i]]+1)
        				//fmt.Println(dp,j,i)
        			}
        		}
        	}
        	// 没找到能装满背包的, 就返回-1
        	if dp[amount] == math.MaxInt32 {
        		return -1
        	}
        	return dp[amount]
        }
        
        // 版本二,先遍历背包,再遍历物品
        func coinChange2(coins []int, amount int) int {
        	dp := make([]int, amount+1)
        	// 初始化dp[0]
        	dp[0] = 0
        	// 遍历背包,从1开始
        	for j := 1; j <= amount; j++ {
        		// 初始化为math.MaxInt32
        		dp[j] = math.MaxInt32
        		// 遍历物品
        		for i := 0; i < len(coins); i++ {
        			if j >= coins[i] && dp[j-coins[i]] != math.MaxInt32 {
        				// 推导公式
        				dp[j] = min(dp[j], dp[j-coins[i]]+1)
        				//fmt.Println(dp)
        			}
        		}
        	}
        	// 没找到能装满背包的, 就返回-1
        	if dp[amount] == math.MaxInt32 {
        		return -1
        	}
        	return dp[amount]
        }
        
        func min(a, b int) int {
        	if a < b {
        		return a
        	}
        	return b
        }
    
  • 279完全平方数

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 和为n,相当于价值目标为n,容量也为n,物品的序列就是完全平方数的序列。每个完全平方数可以使用无数次。所以是完全背包。
  • 讲解观后感

  • dp[j]:和为j的完全平方数的最少数量为dp[j]
  • 递推公式:dp[j] = min(dp[j - i * i] + 1, dp[j]);
  • dp[0]=0非0下标的dp[j]一定要初始为最大值,这样dp[j]在递推的时候才不会被初始值覆盖
  • 本题外层for遍历背包,内层for遍历物品,还是外层for遍历物品,内层for遍历背包,都是可以的,因为不管是组合还是排列都能得到最小值!
  • 出现问题

image.png

  • 问题原因:内层循环物品时有太多冗余,改正方法是将判定条件改为i:=1;i*i<=j;i++
  • 解题代码

  •     // 版本一,先遍历物品, 再遍历背包
        func numSquares1(n int) int {
        	//定义
        	dp := make([]int, n+1)
        	// 初始化
        	dp[0] = 0
        	for i := 1; i <= n; i++ {
        		dp[i] = math.MaxInt32
        	}
        	// 遍历物品
        	for i := 1; i <= n; i++ {
        		// 遍历背包
        		for j := i*i; j <= n; j++ {
        			dp[j] = min(dp[j], dp[j-i*i]+1)
        		}
        	}
        
        	return dp[n]
        }
        
        // 版本二,先遍历背包, 再遍历物品
        func numSquares(n int) int {
        	//定义
        	dp := make([]int, n+1)
        	// 初始化
        	dp[0] = 0
        	// 遍历背包
        	for j := 1; j <= n; j++ {
        		//初始化
        		dp[j] = math.MaxInt32
        		// 遍历物品
        		for i := 1; i*i <= j; i++ { 
        			if j >= i*i {
        				dp[j] = min(dp[j], dp[j-i*i]+1)
        			}
        		}
        	}
        
        	return dp[n]
        }
        
        func min(a, b int) int {
        	if a < b {
        		return a
        	}
        	return b
        }