【深度】动态规划解决背包类问题

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法

1 原理

问题的最优解如果可以由子问题的最优解推导得到,则可以先求解子问题最优解,再构造原问题的最优解;若子问题有较多的重复出现,则可以自底向上从最终子问题向原问题逐步求解

动态规划在经济管理、生产调度、工程技术和最优控制等方面得到了广泛的应用。例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其它方法求解更为方便。比如背包问题(Knapsack problem)、最长公共子串(LCS)等

2 背包问题

一个小偷背了一个背包潜进了金店,包就那么大,他如果保证他背出来所有物品加起来的价值最大,这就是背包问题的来源,背包问题分为01背包问题和完全背包问题

① 0-1背包问题:在最优解中,每个物品只有两种可能的情况,即在背包中或者不在背包中(背包中的该物品数为0或1)

② 完全背包问题:在放入物品时,不限定每个物品的数量,即是完全背包问题

3 问题抽象

现实问题解决的一个关键问题,就是抽象数据模型
n n n个数对 ( w i , v i ) , 0 ≤ i ≤ n (w_i,v_i),0\le i \le n (wi,vi)0in
② 正数 C C C
③ 条件函数 m a x ∑ i = 1 n x i w i ≤ C , x i ∈ { 0 , n } max \sum_{i=1}^{n}x_iw_i \le C, \quad x_i \in \{0,n\} maxi=1nxiwiC,xi{0,n}
④ 目标函数(最大值) ∑ i = 1 n x i v i , x i ∈ { 0 , n } \sum_{i=1}^{n}x_iv_i, \quad x_i \in \{0,n\} i=1nxivi,xi{0,n}

4 状态转换方程

d p ( i , j ) = m a x ( d p ( i − 1 , j ) , d p ( i − 1 , j − w [ i ] ) + v [ i ] ) , j ≥ w [ i ] dp(i,j) = max(dp(i-1, j), dp(i-1, j-w[i]) + v[i]),j \ge w[i] dp(i,j)=max(dp(i1,j),dp(i1,jw[i])+v[i])jw[i]
其中:
i i i,第 i i i件商品
w [ i ] w[i] w[i],第 i i i件商品重量
v [ i ] v[i] v[i],第 i i i件商品价值
j ≤ w [ i ] j \le w[i] jw[i]时,不计算 d p ( i − 1 , j − w [ i ] ) + v [ i ] ) dp(i-1, j-w[i]) + v[i]) dp(i1,jw[i])+v[i])

5 算法例解(0-1背包问题)

条件:
① 四种商品

品类重量(磅)价值(RMB)
音响43000
笔记本电脑32000
吉他11500

② 目标值重量4,求最大价值
③ 流程

背包空间(磅)
放入商品1234
音响
(4, 3000)
0003000
笔记本电脑
(3, 2000)
0020003000
吉他
(1, 1500)
1500150020003500

④ 计算过程
放入第一个物体(音响,4 3000)
空 间 为 1 , 放 不 下 , f ( 1 , 1 ) = 0 空间为1,放不下, f(1,1) = 0 1f(1,1)=0
空 间 为 2 , 放 不 下 , f ( 1 , 2 ) = 0 空间为2,放不下, f(1,2) = 0 2f(1,2)=0
空 间 为 3 , 放 不 下 , f ( 1 , 3 ) = 0 空间为3,放不下, f(1,3) = 0 3f(1,3)=0
空 间 为 4 , 放 得 下 , f ( 1 , 4 ) = 3000 空间为4, 放得下,f(1,4) = 3000 4f(1,4)=3000

放入第二个物体(笔记本电脑, 3 2000)
空 间 为 1 , f ( 2 , 1 ) = m a x ( f ( 1 , 1 ) , f ( 1 , − 2 ) + 2000 ) = m a x ( 0 , 0 ) = 0 空间为1,f(2, 1) = max(f(1,1), f(1,-2) +2000) = max (0, 0) = 0 1f(2,1)=max(f(1,1),f(1,2)+2000)=max(0,0)=0
空 间 为 2 , f ( 2 , 2 ) = m a x ( f ( 1 , 2 ) , f ( 1 , − 1 ) + 2000 ) = m a x ( 0 , 0 ) = 0 空间为2,f(2, 2) = max(f(1,2), f(1,-1) +2000) = max (0, 0) = 0 2f(2,2)=max(f(1,2),f(1,1)+2000)=max(0,0)=0
空 间 为 3 , f ( 2 , 3 ) = m a x ( f ( 1 , 3 ) , f ( 1 , 0 ) + 2000 ) = m a x ( 0 , 2000 ) = 2000 空间为3,f(2, 3) = max(f(1,3), f(1,0) +2000) = max (0, 2000) = 2000 3f(2,3)=max(f(1,3),f(1,0)+2000)=max(0,2000)=2000
空 间 为 4 , f ( 2 , 4 ) = m a x ( f ( 1 , 4 ) , f ( 1 , 1 ) + 2000 ) = m a x ( 3000 , 2000 ) = 3000 空间为4,f(2, 4) = max(f(1,4), f(1,1) +2000) = max (3000, 2000) = 3000 4f(2,4)=max(f(1,4),f(1,1)+2000)=max(3000,2000)=3000

放入第三个物体(吉他,1 1500)
空 间 为 1 , f ( 3 , 1 ) = m a x ( f ( 2 , 1 ) , f ( 2 , 0 ) + 1500 ) = m a x ( 0 , 1500 ) = 1500 空间为1,f(3, 1) = max(f(2,1), f(2,0) +1500) = max (0, 1500) = 1500 1f(3,1)=max(f(2,1),f(2,0)+1500)=max(0,1500)=1500
空 间 为 2 , f ( 3 , 2 ) = m a x ( f ( 2 , 2 ) , f ( 2 , 1 ) + 1500 ) = m a x ( 0 , 1500 ) = 1500 空间为2,f(3, 2) = max(f(2,2), f(2,1) +1500) = max (0, 1500) = 1500 2f(3,2)=max(f(2,2),f(2,1)+1500)=max(0,1500)=1500
空 间 为 3 , f ( 3 , 3 ) = m a x ( f ( 2 , 3 ) , f ( 2 , 2 ) + 1500 ) = m a x ( 2000 , 1500 ) = 2000 空间为3,f(3, 3) = max(f(2,3), f(2,2) +1500) = max (2000, 1500) = 2000 3f(3,3)=max(f(2,3),f(2,2)+1500)=max(2000,1500)=2000
空 间 为 4 , f ( 3 , 4 ) = m a x ( f ( 2 , 4 ) , f ( 2 , 3 ) + 1500 ) = m a x ( 3000 , 3500 ) = 3500 空间为4,f(3, 4) = max(f(2,4), f(2,3) +1500) = max (3000, 3500) = 3500 4f(3,4)=max(f(2,4),f(2,3)+1500)=max(3000,3500)=3500

6 程序例解

按照【5】分析的条件,进行代码验证(注:该程序参考了网上相关代码)

package main

import "fmt"

const (
	// 行
	RAW int = 4
	// 列
	COL int = 5
)

var weight = [RAW]int{0, 3, 4, 1}

var value = [RAW]int{0, 2000, 3000, 1500}

var cells [RAW][COL]int

var selected [RAW]int

// 动态规划计算网格
func dp() {
	for i := 1; i < len(value); i++ {
		for j := 1; j < 5; j++ {
			cells[i][j] = maxValue(i, j)
		}
	}

	for i := 0; i < RAW; i++ {
		fmt.Printf("raw is %v \n", cells[i])
	}
	findBack()
	fmt.Printf("selected goods is %v \n", selected)
}

// 判断当前单元格最大价值方法
func maxValue(i, j int) int {
	// 当前商品无法放入背包,返回当前背包所能容纳的最大价值
	if j < weight[i] {
		return cells[i-1][j]
	}
	
	// 可放进背包时候,计算放入当前商品后的最大价值
	curr := value[i] + cells[i-1][j-weight[i]]
	if curr >= cells[i-1][j] {
		return curr
	}
	return cells[i-1][j]
}

// 回溯选择的商品方法
func findBack() {
	col := COL - 1
	for i := RAW - 1; i > 0; i-- {
		if cells[i][col] > cells[i-1][col] {
			selected[i] = 1
			col = col - weight[i]
		} else {
			selected[i] = 0
		}
	}
}

func main() {
	dp()
}

raw is [0 0 0 0 0]
raw is [0 0 0 2000 2000]
raw is [0 0 0 2000 3000]
raw is [0 1500 1500 2000 3500]
selected goods is [0 1 0 1]
sandbox> exited with status 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值