算法--动态规划
动态规划
-
定义
动态规划(dynamic programming, 简称DP): 寻找多分支下最优解 -
工作原理
要解决一个复杂的问题,先解决其子问题(先解决子问题,再逐步解决大问题)
这便是典型的递归思想,eg:斐波拉切数列 -
应用场景
斐波拉切数列定义如下: F(0) = 1, F(1) = 1, F(n) = F(n-1)+F(n-2) (n>=2, n属于任意正整数)
代码实现:
def fib(i: int):
if i <= 1:
return 1
return fib(i-1) + fib(i-2)
【缺点】
当n取足够大的值,程序运行费时,且迭代次数太多,函数堆栈溢出。
优化方案:
用一个数字来保存曾经计算过的数据,避免重复计算动态规划
- 经典问题
1、 背包问题
0-1 背包
【question】 一个容量为v的背包和一些物品。物品有两种属性,体积w和价值v,
每种物品只有一个。要求:背包装下价值尽可能多的物品,求该最大价值,背包
可以不被装满。
0-1背包问题: 在最优解中,每个物品中只有两种可能,在背包中和不在背包中。
步骤1【找子问题】:
子问题和物品相关, 每个物品,有两种结果: 能装下和不能装下。
> a. 报的容量比物品体积小,装不下,此时,最大价值和前 i-1 个物品的
最大价值一样。
> b. 容量足够装下该物品,但是装了不一定大于当前相同体积的最优值,需要
进行比较。因此,子问题中物品数和背包容量都应该被当作变量。 子问题
确定为背包容量时,求前i个物品所能达到的最大价值。
步骤2【确定状态】:“状态”对应的“值” 即为背包容量时,求前i个物品所能达到的最大
价值设为dp[i][j]。初始,dp[0]j为0, 没有物品也没有价值。
步骤3【确定状态转移方程】:第i个物品体积为w[i]价值为v[i], 则状态转移方程为:
* j<w, dp[i][j] = dp[i-1][j] //背包装不下该物品, j: 当前背包容量
* j>=w, dp[i][j] = max
from typing import List
def dynamic_p()->List:
items = [ # 物品项
{"name": "水", "weight": 3, "value": 10},
{"name": "书", "weight": 1, "value": 3},
{"name": "食物", "weight": 2, "value": 9},
{"name": "小刀", "weight": 3, "value": 4},
{"name": "衣物", "weight": 2, "value": 5},
{"name": "手机", "weight": 1, "value": 10}
]
max_capacity = 6 # 约束条件为 背包最大承重为6
dp = [[0] * (max_capacity + 1) for _ in range(len(items) + 1)]
for row in range(1, len(items) + 1): # row 代表行
for col in range(1, max_capacity + 1): # col 代表列
weight = items[row - 1]["weight"] # 获取当前物品重量
value = items[row - 1]["value"] # 获取当前物品价值
if weight > col: # 判断物品重量是否大于当前背包容量
dp[row][col] = dp[row - 1][col] # 大于直接取上一次最优结果 此时row-1代表上一行
else:
# 使用内置函数max(),将上一次最优结果 与 当前物品价值+剩余空间可利用价值 做对比取最大值
dp[row][col] = max(value + dp[row - 1][col - weight], dp[row - 1][col])
return dp
- 完全背包问题
有N种物品和一个容量为V的背包,每种物品都有无限件可用。
第i种物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,
且价值总和最大。
from typing import List
class Thing(object):
"""
物品类, 用于定义数据结构,
当数据两比较大时,通常使用类,来定义数据结构
"""
def __init__(self, weight, value):
self.weight = weight
self.value = value
def dynamic01(things: List[Thing], capacity: int):
"""
01 背包问题
:param weight:
:param value:
:return:
"""
n = len(things) # 物品个数
v = capacity # 背包容量
f = [0] * v # 总价值
# 优化前 时间空间负责度为: v*n
# for i in range(n):
# for j in range(v, -1, -1):
# f[j] = max(f[j], f[j - things[i].weight] + things[i].value)
# 时间没有优化空间,空间优化后为n
for i in range(n):
for j in range(v, things[i].weight, -1):
f[j-1] = max(f[j-1], f[j - things[i].weight - 1] + things[i].value)
return max(f)
def dynamic02(N: int, V: int, things:List[Thing]):
"""
完全背包问题
:param N: 物品种类
:param V: 背包容量
:param things: 物品详情
:return:
"""
if N == 0 or V == 0:
return 0
dp = [0] * (V + 1)
for i in range(N):
for j in range(things[i].weight, V, 1):
dp[j] = max(dp[j], dp[j-things[i].weight] + things[i].value)
return dp[V]
def to_int(num_list: List):
"""
将输入的字符转换成数字
:param num_list:
:return:
"""
if not num_list:
return []
if isinstance(num_list[0], list):
res = []
for l in num_list:
res.append(to_int(l))
return res
else:
return [int(num) for num in num_list]
def main():
print(f"01背包:\n")
n = input("请输入总物品数N:")
v = input("背包容积V:")
n = int(n)
v = int(v)
array = list()
print("请输入物品体积、价值(使用空格隔开)\n")
while n > 0:
arr = input()
if arr is None:
continue
array.append(arr.strip().split(' '))
n -= 1
array = to_int(array)
things = []
for arr in array:
thing = Thing(arr[0], arr[1])
things.append(thing)
max_value = dynamic01(things, v)
print(max_value)