动态规划 ---- python:简单背包问题

1. 问题描述:有1 背包能装进4千克物体,吉他重1千克,价值1500;猫王音箱重4千克,价值3000;笔记本重3千克,价值2000,求这个背包所能装的物体的最大价值。

最简单的算法(尝试各种物品的组合,再找出价值最高的组合),但是随着物品数量的增加,求解速度会非常慢(n件物品的组合数:2^n)。

另一种方法----动态规划(先解决子问题,再解决大问题)。对于背包问题,先解决子背包问题,再解决原问题。每个动态规划问题都从一个网格开始,背包问题的网格如下表(各行为:可选择的商品,各列为:不同容量的背包):

特征:

      背包具有的属性为--------重量(kg)

      商品具有的属性为--------容量(kg)和价格(¥)

将背包和商品都有的属性(kg)进行细分,背包化为不同容量的背包(如容量为1kg、2kg、3kg、4kg的背包)。随着可选择物品的增多,每一列的数值为所对应背包所能容纳的价值(最后一位为最大价值)。

 背包1kg2kg3kg4kg
商品00000
吉他1kg01500150015001500
音箱4kg01500150015003000
笔记本3kg0150015002000 

设橙黄色区域为矩阵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];
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值