北大硕士LeetCode算法专题课--动态规划和贪心算法

80 篇文章 2 订阅
16 篇文章 1 订阅

面试专题课:

北大硕士LeetCode算法专题课--递归和回溯_骨灰级收藏家的博客-CSDN博客

北大硕士LeetCode算法专题课-栈、队列相关问题_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课--链表相关问题_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课-查找相关问题_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课-字符串相关问题_骨灰级收藏家的博客-CSDN博客

北大硕士LeetCode算法专题课-数组相关问题_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课-基础算法查找_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课-基础算法之排序_骨灰级收藏家的博客-CSDN博客 

北大硕士LeetCode算法专题课---算法复杂度介绍_骨灰级收藏家的博客-CSDN博客 

什么是动态规

我们以斐波那契数列为例来说明什么是动态规划

斐波那契数列(Fibonacci sequence: F(0)=0F(1)=1, F(n)=F(n - 1)+F(n - 2)

import time
def fib(n):
if n == 0:
return 0
if n == 1:
return 1
return fib(n - 1) + fib(n - 2)

if  		name 	 == ' 	main 	':  t1 = time.time()
n = fib(10)
t2 = time.time()
print("n = %d, 用时%d 毫秒" % (n, (t2 - t1) * 1000))

 

 

 

 

斐波那契数列,优化: 记忆化搜索

def fib2(n):
global num  num += 1  if n == 0:
return 0
if n == 1:
return 1
if res[n] == -1:
res[n] = fib2(n - 1) + fib2(n - 2)
return res[n]
if 	name	== ' 	main 	':
res = [-1 for i in range(41)]  t1 = time.time()
n = fib2(40)
t2 = time.time()
print("n = %d, 用时%d 毫秒,执行了%d次" % (n, (t2 - t1) * 1000, num))

 记忆化搜索:自上向下的解决问题

def fib2(n):
global num  num += 1  if n == 0:
return 0
if n == 1:
return 1
if res[n] == -1:
res[n] = fib2(n - 1) + fib2(n - 2)
return res[n]

动态规划:把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,在解决小问题的时候,  记忆每一个小问题的答案,使得每个小问题只解决一次,最终达到解决原问题的效果。

爬楼梯 (LeetCode 70)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 

每次你可以爬 1 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?提示:1 <= n <= 45

解题思路:假设f(n) 表示爬 n 阶楼梯可能的走法。

自顶向下的思路: 假设当前站在第n阶楼梯上,那么上一步可能在第n-1 或者 n-2 , 分别需要爬1级台阶和2级台阶 所以 f(n) = f(n-1) + f(n-2)

代码实现1 暴力搜索 上面式子中 n-2>=0 所以 最后一次递归调用为 f(2) = f(1) + f(0),边界就是 f(1) = 1f(0) = 1

def climbStairs(n):
if n == 0 or n == 1:
return 1
return climbStairs(n - 1) + climbStairs(n - 2)

 代码实现2 记忆化搜索

def climbStairs2(n):  def dfs(i, memo):
if i == 0 or i == 1:
return 1
if memo[i] == -1:
memo[i] = dfs(i - 1, memo) + dfs(i - 2, memo)
return memo[i]

return dfs(n, [-1] * (n + 1))

 代码实现3 动态规划

def climbStairs3(n):  res = [0] * (n + 1)  res[0] = res[1] = 1
for i in range(2, n + 1):
res[i] = res[i - 1] + res[i - 2]
return res[-1]

整数拆分 (LeetCode 343)

假设给定一个正整数 n ,将其拆分为 k 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 2 <= n <= 58

 

 

 

def integerBreak(n):  def dfs(n, memo):
if n ==1:
return 1
if memo[n] !=-1:  return memo[n]
res = -1
for i in range(1,n):
res = max(res,i*(n-i),i*dfs(n-i,memo))  memo[n] = res
return res
return dfs(n,[-1] * (n + 1))
def integerBreak(n):  dp = [0] * (n + 1)
for i in range(2, n + 1):
for j in range(i):
dp[i] = max(dp[i], j * (i - j), j * dp[i - j])
return dp[n]

打家劫 (LeetCode 198)

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

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

先考虑最简单的情况。如只有一间房屋,则偷窃该房屋,可以偷到最高总金额。如只有两间房屋,则由于两间房屋相邻,  不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。

如果房屋数量大于两间,应该如何计算能够偷窃到的最高总金额呢?

对于第 k (k>2) 间房屋,有两个选项:

偷窃第 k 间房屋,那么就不能偷窃第 k-1 间房屋,偷窃总金额为前 k-2 间房屋的最高总金额与第 k 间房屋的金额之和。

不偷窃第 k 间房屋,偷窃总金额为前 k-1 间房屋的最高总金额。

在两个选项中选择偷窃总金额较大的选项,该选项对应的偷窃总金额即为前 k 间房屋能偷窃到的最高总金额。 用 dp[i] 表示前 i 间房屋能偷窃到的最高总金额,那么就有如下的状态转移方程:

def rob(nums):  if not nums:
return 0

size = len(nums)
if size == 1:
return nums[0]

dp = [0] * size  dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, size):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])

return dp[size - 1]

 发饼干 (LeetCode 455)

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。 如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。

你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择,就能得到问题的答案。贪心 算法需要充分挖掘题目中条件,没有固定的模式,解决有贪心算法需要一定的直觉和经验。

为了尽可能满足最多数量的孩子,从贪心的角度考虑,应该按照孩子的胃口从小到大的顺序依次满足每个孩子,且 对于每个孩子,应该选择可以满足这个孩子的胃口且尺寸最小的饼干。

首先对数组 g s 排序,然后从小到大遍历 g 中的每个元素,对于每个元素找到能满足该元素的 s 中的最小的元素。 具体而言,令 i g 的下标,j s 的下标,初始时 i j 都为 0,进行如下操作。

对于每个元素 g[i],找到未被使用的最小的 j 使得 g[i]≤s[j],则 s[j] 可以满足 g[i]。由于 g s 已经排好序,因此整个过 程只需要对数组 g s 各遍历一次。当两个数组之一遍历结束时,说明所有的孩子都被分配到了饼干,或者所有的 饼干都已经被分配或被尝试分配(可能有些饼干无法分配给任何孩子),此时被分配到饼干的孩子数量即为可以满 足的最多数量。

def findContentChildren(g, s):  g.sort()
s.sort()
n, m = len(g), len(s)  i = j = count = 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值