动态规划-背包问题、兑换零钱问题、旅行商问题
来源:互联网 发布:等一分钟网络女歌手 编辑:程序博客网 时间:2024/04/29 05:19
背包问题
问题介绍
假定需要将Goods={g1,g1,…gn}放入容量为ALL的背包内,volumej代表第i个物品的体积,valuej代表第j个物品的价值。我们要把这些物品装进背包,这些物品的体积不超过ALL,而且要使它们的总价值达到最大。因为背包不可包含一个以上的同类物品,所以一般称这样的背包问题为0/1背包问题,完全背包问题类似于兑换零钱问题。
解决思路
设C[i,j]表示从前i项{g1,g1,…gn}取出来装入体积为j的背包的物品的最大价值,其中i∈[0,n],j∈[0,ALL],最终chart[n,ALL]是我们最终想要得到的结果。可以通过观察得到以下结论:
C[i,j]为下面两个量的最大值。
①V[i-1,j]:使用最优的方法将{g1,g1,…gi-1}放入容量为j的背包所得的最大价值。
②V[i-1,j-volumei]+value[i]:使用最优的方法将{g1,g1,…gi-1}放入容量为j-volumei的背包所得的最大价值再加上物品gi的价值。递推式
C(i,j)=⎧⎩⎨0,C[i−1,j],max(C[i−1,j],C[i−1,j−volumei]+valuei)若i=0 或 j=0若j<volumei若i>0和j≥volumei
Python
使用标志矩阵输出装在背包里的物品
# coding = utf-8class Goods: def __init__(self, name=None, volume=0, value=0): self.name = name self.volume = volume self.value = value def get_name(self): return self.name def get_volume(self): return self.volume def get_value(self): return self.valuedef knapasck(lst, all): """ 利用动态规划求解背包问题 :param lst: :param all: :return: """ chart = [[0 for col in range(all + 1)] for row in range(lst.__len__() + 1)] # 初始化结果矩阵,并将其值全部置为0 chart_flag = [[[] for col in range(all + 1)] for row in range(lst.__len__() + 1)] # 初始化标志矩阵,并将其值全部置为[] for row in range(lst.__len__() + 1): for col in range(all + 1): if 0 == row or 0 == col: chart[row][col] = 0 elif col < lst[row - 1].get_volume(): chart[row][col] = chart[row - 1][col] chart_flag[row][col] = chart_flag[row - 1][col] elif row > 0 and col >= lst[row - 1].get_volume(): chart[row][col] = max(chart[row - 1][col], chart[row - 1][col - lst[row - 1].get_volume()] + lst[row - 1].get_value()) if chart[row][col] == chart[row - 1][col]: chart_flag[row][col] = chart_flag[row - 1][col] else: chart_flag[row][col] = chart_flag[row - 1][col - lst[row - 1].get_volume()] + [lst[row - 1].get_name()] return chart, chart_flagif __name__ == "__main__": goods_lst = [ Goods('a', 2, 3), Goods('b', 3, 4), Goods('c', 4, 5), Goods('d', 5, 7)] knapasck_chart, knapasck_flag = knapasck(goods_lst, all=9) # 显示计算列表和标记列表 print('结果矩阵如下:') for i in knapasck_chart: print(i) print("标志矩阵如下:") for j in knapasck_flag: print(j)
运行结果
兑换零钱问题
问题介绍
假设2角、3角、5角和6角硬币无限个,给一个固定的金额,求可置换硬币的若干个方法,类似于完全背包问题,即物品在条件允许下可以选择任意个。
解决思路
num[i][j]表示前i个硬币兑换的金额为j时一共有多少种兑换方法,coins代表硬币的种类(降序排序好的列表)。
为了计算方便,我们假设兑换金额为0时有一种方法,那就是什么都不选。
我们最终得到以下结论
①如果需要兑换的金额小于当前i表示硬币的面值,则num[i][j]的值为num[i-1][j];
②如果需要兑换的金额大于当前j表示硬币的面值,则num[i][j]的值为num[i-1][j]+num[i-1][j-a[i]]。递推式
num(i,j)={num[i−1][j],num[i−1][j]+num[i][j−coins[i]]若j<coins[i]若j≥coins[i]
Python
获得零钱兑换的方法数
def money_change(lst, money=0): """ 兑换零钱 :param lst:零钱面值 :param money:金额 :return: """ num = [[0 for col in range(money + 1)] for row in range(lst.__len__())] # 初始化结果矩阵,并将其值全部置为1 for row in range(0, lst.__len__()): num[row][0] = 1 # 设定第一列为1,表示金额为0时,仍有一种兑换方法 for row in range(1, lst.__len__()): for col in range(money + 1): if col < lst[row]: num[row][col] = num[row - 1][col] else: num[row][col] = num[row - 1][col] + num[row][col - lst[row]] return numif __name__ == "__main__": coins = [0, 2, 3, 5, 6] num_lst = [] min_num = 0 for element in money_change(coins, 10): print(element)
运行结果
求解实际解
采用递归的方法可以求出兑换的所有解以及最优解。
Python
# coding = utf-8ALL_METHOD = [] # 定义全局变量保存所有解决办法class Method: info = '' length = 0 def __init__(self, info, length=0): self.info = info self.length = lengthdef money_change(lst, method_lst, money=0): """ 兑换零钱 :param lst:零钱面值 :param method_lst: 解决办法列表 :param money:金额 :return: """ temp = [] # 用于储存当前计算的结果 global ALL_METHOD # 声明为全局变量 if money > 0 and len(lst) > 0: temp.extend(method_lst) if len(lst) > 1: money_change(lst[0: len(lst) - 1], temp, money) if money >= lst[len(lst) - 1]: method_lst.append(lst[len(lst) - 1]) temp.clear() temp.extend(method_lst) money_change(lst[0: len(lst)], temp, money - lst[len(lst) - 1]) elif money <= 0: ALL_METHOD.append(Method(method_lst, len(method_lst)))if __name__ == "__main__": coins = [2, 3, 5, 6] num_lst = [] min_num = 0 money_change(coins, [], 10) for element in ALL_METHOD: print(element.info) ALL_METHOD.sort(key=lambda method: method.length) # 排序 for element in ALL_METHOD: if element.length == ALL_METHOD[0].length: print("兑换的最小个数为{},组成为:{}".format(element.length, sorted(element.info)))
运行结果
使用这个方法求解每类硬币数量有限时,我们只需对递推式做出修改便能得到结果,类似于0/1背包问题。
num(i,j)={num[i−1][j],num[i−1][j]+num[i−1][j−coins[i]]若j<coins[i]若j≥coins[i]
修改下列代码
money_change(lst[0: len(lst)], temp, money - lst[len(lst) - 1])
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
money_change(lst[0: len(lst) - 1 ], temp, money - lst[len(lst) - 1])
运行结果
旅行商问题【待完成】
满足三角不等式的旅行商问题
即满足c(u,w)≤c(u,v)+c(v,w)
一般旅行商问题
其他旅行商问题
- 动态规划-背包问题、兑换零钱问题、旅行商问题
- 动态规划--零钱问题
- 动态规划之找零钱问题与背包问题
- 零钱兑换问题
- 动态规划:找零钱问题
- 动态规划 找零钱问题
- 动态规划找零钱问题
- 旅行商问题的动态规划解决
- 旅行商问题(动态规划)
- 动态规划求解TSP(旅行商)问题
- 状态压缩动态规划 -- 旅行商问题
- 动态规划思想求旅行商问题
- 动态规划 解TSP旅行商问题
- 动态规划 之 旅行商问题 TVP
- 使用动态规划求解旅行商问题
- 动态规划 背包问题
- 【动态规划】背包问题
- 动态规划-背包问题
- NLP 中的embedding layer
- vi 全部命令
- 【图论】[luoguP1330]封锁阳光大学
- BZOJ1025(SCOI2009)[游戏]--线性筛+DP
- MySQL 第八篇:自定义函数、存储过程、游标
- 动态规划-背包问题、兑换零钱问题、旅行商问题
- pandas中使用三元表达式
- oracle死锁
- 用scikit-learn学习聚类算法
- <VRTK学习一> 初识vrtk,如何在自己的项目中使用vrtk
- mysql循环语句(loop、while ....do..、repeat),
- js将后端返回的数据动态添加到table中
- 51nod-1185【博弈论】威佐夫游戏【未完】
- 选择器基础知识