1. 问题描述:有1 背包能装进4千克物体,吉他重1千克,价值1500;猫王音箱重4千克,价值3000;笔记本重3千克,价值2000,求这个背包所能装的物体的最大价值。
最简单的算法(尝试各种物品的组合,再找出价值最高的组合),但是随着物品数量的增加,求解速度会非常慢(n件物品的组合数:2^n)。
另一种方法----动态规划(先解决子问题,再解决大问题)。对于背包问题,先解决子背包问题,再解决原问题。每个动态规划问题都从一个网格开始,背包问题的网格如下表(各行为:可选择的商品,各列为:不同容量的背包):
特征:
背包具有的属性为--------重量(kg)
商品具有的属性为--------容量(kg)和价格(¥)
将背包和商品都有的属性(kg)进行细分,背包化为不同容量的背包(如容量为1kg、2kg、3kg、4kg的背包)。随着可选择物品的增多,每一列的数值为所对应背包所能容纳的价值(最后一位为最大价值)。
背包 | 1kg | 2kg | 3kg | 4kg | |
商品 | 0 | 0 | 0 | 0 | 0 |
吉他1kg | 0 | 1500 | 1500 | 1500 | 1500 |
音箱4kg | 0 | 1500 | 1500 | 1500 | 3000 |
笔记本3kg | 0 | 1500 | 1500 | 2000 |
设橙黄色区域为矩阵Arr,通过总结观察,则矩阵中每一个位置的价值,可以通过以下公式进行计算(i为行下标,j为列下标):
if wi(当前物品的重量) <= j(子背包容量1,2,3,4): Arr[i, j] = max(Arr[i-1, j], Value(当前物品的价值)[i]+Arr[i-1, j-wi(当前物品重量)]) else: Arr[i, j] = Arr[i-1, j]
2.程序
import numpy as np
def solve(vlist,wlist,totalWeight,totalLength):
resArr = np.zeros((totalLength+1,totalWeight+1),dtype=np.int32)
for i in range(1,totalLength+1):
for j in range(1,totalWeight+1):
if wlist[i] <= j:
resArr[i,j] = max(resArr[i-1,j-wlist[i]]+vlist[i],resArr[i-1,j])
else:
resArr[i,j] = resArr[i-1,j]
return resArr[-1,-1],resArr
if __name__ == '__main__':
v = [0,1500,3000,2000]
w = [0,1,4,3]
weight = 4
n = 3
result,res = solve(v,w,weight,n)
print("所能装的最大价值为:",result)
print(res)
'''
所能装的最大价值为: 3500
[[ 0 0 0 0 0]
[ 0 1500 1500 1500 1500]
[ 0 1500 1500 2000 3500]
[ 0 1500 1500 2000 3500]]
'''
java程序:
// arr为物品的重量 val为物品对应的价值 n为背包的重量
public static int solve(int[] arr, int[] val, int n) {
int k = arr.length;
int[][] dp = new int[k+1][n+1];
for(int i=1; i<n+1; i++) { // 遍历列
for(int j=1; j<k+1; j++) { // 遍历行
if((i-arr[j-1])>= 0) { // 当前背包i重量 >= 本行的物品重量时
dp[j][i] = Math.max(dp[j-1][i], val[j-1]+dp[j-1][i-arr[j-1]]);
}else { // 当前背包i重量 < 本行的物品重量时
dp[j][i] = dp[j-1][i];
}
}
}
return dp[k][n];
}
问题2:凑钱问题
给你k种面值的硬币,面值分别为c1,c2,...,ck,再给你一个总金额n,问最少需要几枚硬币抽出这些金额,若不能凑出,则回答-1。例子:若面值为[1,2,5] ,k=3,总金额 n=11,那么最少需要3枚硬币。(5+5+1)
解答:
最优子结构性质:原问题的解由子问题的最优解构成。(子问题间必须相互独立,不相互影响)
设f(n)为凑够金额n所需的最小次数
写出动态转移方程:
当 n=0时,f(0) =0;
当 n>0时,f(n) = 1 + min{f(n-ci)| 1<=i<=k}
例如:f(11) 可以由f(11-1),f(11-2),f(11-5)的最优解转化而来。
public static int coinChange(int[] arr, int count) {
int[] dp = new int[count+1];
Arrays.fill(dp,count+1); // 初始化dp
dp[0] = 0;
for(int i=0; i<dp.length; i++) {
for(int a:arr) {
if(i-a < 0) continue;
dp[i] = Math.min(dp[i], 1+dp[i-a]);
}
}
return (dp[count] == count+1) ? -1:dp[count];
}