经典动态规划——0/1背包问题(二维数组动规,一维数组动规实现)

本文介绍了使用动态规划解决0/1背包问题,包括二维数组和一维数组两种实现方式。通过状态转移方程,分析如何找到背包内物品最大总价值,并给出具体代码示例。
摘要由CSDN通过智能技术生成

题目描述

有为N件物品,它们的重量w分别是w1,w2,…,wn,它们的价值v分别是v1,v2,…,vn,每件物品数量有且仅有一个,现在给你个承重为M的背包,求背包里装入的物品具有的价值最大总和?

输入描述:
物品数量N=5件
重量w分别是:2 2 6 5 4
价值v分别是:6 3 5 4 6
背包承重为M=10

输出描述:
背包内物品最大总和为15
示例1
输入
5
10
2 2 6 5 4
6 3 5 4 6
输出
15

分析

对于这类问题,动态规划是最佳的选择,每一次求出局部的最优解,便可以求出整体的最优解,比如上述的题目描述,他给出的背包的大小是10,那么我们只需要依次求出来背包容量从1到10之间(包含1和10),存放这五个物体既可以放下又能够取得最大价值即可,这里优先想到的是用一个二维数组来存储每一次的最优解,dp[i][j],

dp[i][j] 表示当前背包大小容量为j的情况下存放第i个物品的最优解。
用二维数组来解决该问题的话,其状态转移方程如下
前提:j<goods[i]
dp[i][j] = dp[i+1][j]
前提:j>=goods[i]
dp[i][j] = max(dp[i + 1][j - w[i]] + v[i] , dp[i+1][j])

下面进行具体分析:
如果想要用动态规划解决问题,首要的步骤就是初始化,其次是找到状态转移方程,那么这个问题的初始化,我们可以这样想,我想要初始化,那么我先装进去一个物品,只要这个物品比当前的背包容量小于等于,那么我就可以装进去,并且可以将这个物品的价值记录下来,否则当前物品的重量大于背包的重量则装不进去,很显然,这时候的价值为0值得注意的是,我们需要从最后一个物品开始装,倒着向上走。

上述题目示例中,我们先来初始化最后一行,可以得到下图
在这里插入图片描述
因为最后一个物品的重量为4,价值为6,那么只要当前背包的容量大于等于4的情况下,就可以放下这个物品,取得当前最优的价值为6。这里如果题目要求只有这一个物品,那么很显然取该行的任何一个值都是当前容量下所能达到的最大价值。可惜往往事与愿违,我们需要放下倒数第二个物品,这里就需要用到了上述的状态转移方程。
我们先看得到的图如下,再具体分析
在这里插入图片描述
首先我们发现再j > 5 (这里不包含j=5) 之前,背包的容量都是小于i = 4这个物品的重量的那么就执行:
dp[i][j] = dp[i - 1]
这里可以理解成我当前的容量放不下该物品,那么我只能继承我下面的那已经放好的若干个物品的最优解
当j>=5的时候,我们重点来看:
j=5,意味着背包的容量从现在开始,已经可以放得下这个物品了,那么现在开始就需要考虑是否要取出某个物品后放入该物品能取得更大的价值,我们先假设把w=4,v=6的物品取出来,然后把这个物品放进去,结果发现放入这个物品的后价值变为了4,比原来的6要低,因此不放进去能取得j=5的条件下的最优解,仍然是6。
j=(6,7,8)的时候,同理带入状态转移方程
dp[i][j] = max(dp[i + 1][j - w[i]] + v[i] , dp[i+1][j])
比如j = 7
dp[4][7] = max(dp[4+1][7 - 4] + w[4] , dp[4+1][7])
dp[4+1][7 - 4] + w[4]表示:我现在先取得能放下这个物品的情况下的最优解,再加上这个物品的价值。
很显然,如果dp[5][3]已经取得了最优解的话,那么dp[4][7]就一定能取得最优解,这里也正是为什么要从下向上走的主要原因。
dp[4+1][7]表示:我步放入这个物品的情况下的最优解。

再看j=9的时候,
我需要看dp[4 + 1][9 - 4]的最优解,发现是6,那么我再放入该物品即+v[4],则可以取到价值为10,那如果不放入这个物品则继承之前的最优解dp[4 + 1][9]发现价值只能是6,通过max比较发现,当前最优的解为10,同理j=10也是如此。

因此,只需要通过上述的方法,将剩余物品都遍历一遍,就可以得到如下最优解的图
在这里插入图片描述

代码

#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
   
    int goods_num = 0;//货物总数
    int values_num = 0;
  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值