刷题笔记——动态规划(一)

刷题笔记——动态规划(一)

动态规划在算法中是比较难的一类问题。

深受其动态状态转移方程的折磨。

今天带来四道动态规划中等题目,解题思路差不多,当做动态规划敲门砖了。

322. Coin Change

题目

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

Note:

You may assume that you have an infinite number of each kind of coin.

题目大意

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

解题思路

这道题诈一看有点似贪心算法,但是直接使用贪心策略,可能得到的不是最优解。

因此直接采用动态规划常规步骤

  1. 创建dp数组,大小为amount+1,并将其初始化(除了dp[0]以外其他都为amount+1)

  2. 遍历一遍dp数组,从1 ~ amount

  3. 根据状态转移方程向dp数组内填值

    dp[i] 代表的是 总金额i的最少硬币个数
    
    i金额最少硬币数肯定等于(i减去各个面额硬币的金额最小的那个+1)
    
    由此可得状态转移方程:
    dp[i] = min(dp[i], dp[i-coins[j]]+1)
    
  4. 返回dp[amount]

因此,这道题直接得出解题。上代码:

func coinChange(coins []int, amount int) int {
	dp := make([]int, amount+1)
	for i := 1; i < len(dp) ; i++ {
		dp[i] = amount + 1
	}
	for i := 1; i <= amount; i++ {
		for _, v := range coins {
			if v <= i {
				dp[i] = min(dp[i], dp[i-v]+1)
			}
		}
	}
	if dp[amount] > amount {
		return -1
	}
	return dp[amount]
}

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

第二道题也和此题解题方法差不多

300. Longest Increasing Subsequence

题目

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Note:

  • There may be more than one LIS combination, it is only necessary for you to return the length.
  • Your algorithm should run in O(n^2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

题目大意

给定一个无序的整数数组,找到其中最长上升子序列的长度。

解题思路

这道题和上题的思路一样,唯一区别在于需要保存当前最大值,最终返回这个最大值

上代码:

func lengthOfLIS(nums []int) int {
	dp := make([]int, len(nums))
	for i := 0; i < len(nums); i++ {
		dp[i] = 1
	}
    //res用作保存最大值
	res := dp[0]
	for i := 1; i < len(dp); i++ {
		for j := 0; j < i; j++ {
            //当前值大于之前的某个值 那么dp[i]等于dp[之前值下标]+1和dp[i]较大的一个
			if nums[i] > nums[j] {
                //状态转移方程
				dp[i] = max(dp[i], dp[j]+1)
			}
		}
		res = max(res, dp[i])
	}
	return res
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

下一道是leetcode279题,完全平方数

279. perfect-squares

给定正整数 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
解题思路

这道题也是同样的思路,直接上手即可

上代码:

func numSquares(n int) int {
	dp := make([]int, n+1)
	for i := 1; i < n+1; i++ {
		dp[i] = n+1
	}
	for i := 1; i < n+1; i++ {
		for j := 1; j <= i; j++ {
			if i - j*j < 0 {
				break
			}
			dp[i] = min(dp[i], dp[i-j*j]+1)
		}
	}
	return dp[n]
}

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

最后一道是较为出名的打家劫舍问题:

198. house-robber

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
    偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。
解题思路

同样 使用res记录最大值,其他步骤和之前一样即可。

func rob(nums []int) int {
	dp := make([]int, len(nums))
	dp[0] = nums[0]
	res := dp[0]
	for i := 1; i < len(dp); i++ {
		dp[i] = nums[i]
		for j := 0; j < i-1 ; j++ {
			dp[i] = max(dp[i], dp[j]+nums[i])
		}
		res = max(res, dp[i])
	}
	return res
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

至此,四道中等难度的动态规划就完成了。同样的方式,同样的解题。


有兴趣的关注我的公众号,一起学习交流啊
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值