令人头疼的背包九讲(2)完全背包问题

点击上方“Jerry的算法和NLP”,选择“星标”公众号

      重磅干货,第一时间送达

 

     背包问题是一个经典的动态规划模型。它既简单形象容易理解,又在某种程度上能够揭示动态规划的本质,故不少教材都把它作为动态规划部分的第一道例题.

题目


完全背包问题

题目要求

  

  有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值

1≤n≤100

1≤wi,vi≤100

1≤W≤10000

输入:

整数用空格隔开 

第一行有两个整数N,V 代表物品种数和背包容积

接下来有N行,每行两个整数用空格隔开,代表物品体积和价值

输出:

在背包容量下能够最大化的价值

输入样例
4 6
1 1
2 3
3 4
4 5

输出样例:
9

 

分析

与零一背包不同的是,零一背包中的物品是不可以重复拿取的,只可以拿取当前物品或者不拿取当前物品,不可以拿取多个,完全背包的物品是可以任意拿取多个的来构成不超过背包容量并且构成的总价值是最大的

动态规划的核心是找到dp公式或者状态转移的方程,理解清楚中间的过程是怎么样进行变化的,因为动态规划总是要利用到之前历史上的最佳方案,所以dp数组里面存储的肯定是历史上存储的最佳方案,一开始的时候我们是可以借助excel表格来帮助我们理解dp数组是怎样生成的

我们可以这样想:当前我的背包容量有多少,而且我可以选择的物品的范围是什么,那么这两个变量就可以确定一个值了,我们可以计算出当前背包容量对于当前可以选择的物品范围构成的最大价值

解题步骤

表格说明
  • 建立一个二维数组f[i][j],定义为i为遍历到第i个物品的时候,j为背包的容量大小,j最大为m,如f[2][3]指的就是遍历到第二个物品下容量为3的最大价值


表格

0‍12345
0000000
1000000
2000000
3000000
4000000

由输入可知第一个物品为1 1 即价值为1,所占的容量也为1。


根据f[i][j]=max(f[i][j-w[i]]+v[i])可知,从第一个物品开始,由于今天这个问题属于完全背包问题,故两层循环中,第一层循环为遍历物品,第二层循环为从小到大扩大背包容量不断塞进更多的当前物品。


f[1][0]=max(f[1][0],f[1][0-1]+2) 不执行
执行 f[1][0]=f[0][0]
从上式可以知道我我们取不到f[1][0-1] 故加个判断j>=w[i]即可,若满足就可以加入物品,不满足就不更新仍维持0


f[1][1]=max(f[1][1],f[1][1-1]+2)
f[1][2]=max(f[1][2],f[1][2-1]+2)
f[1][3]=max(f[1][3],f[1][3-1]+2)
f[1][4]=max(f[1][4],f[1][4-1]+2)
f[1][5]=max(f[1][5],f[1][5-1]+2)

这样第一个物品结束后,我们来看看整个二维数组的更新情况


012345
0000000
1012345
2000000
3000000
4000000

f[1][5]就表示了在选择第一个物品的时候,容量为5的情况下的最大价值为5


接下来更新第二个物品

加个判断j-w[i]>=0,若满足就可以加入物品,不满足就不更新仍维持0

f[2][0]=max(f[2][0],f[2][0-2]+3) 不执行
故执行f[2][0]=f[1][0]
f[2][1]=max(f[2][1],f[2][1-2]+3) 不执行
故执行f[2][1]=f[1][1]

f[2][2]=max(f[2][2],f[2][2-2]+3)
f[2][3]=max(f[2][3],f[2][3-2]+3)
f[2][4]=max(f[2][4],f[2][4-2]+3)
f[2][5]=max(f[2][5],f[2][5-2]+3)

这样第二个物品结束后,我们来看看整个二维数组的更新情况


012345
0000000
1012345
2013467
3000000
4000000

以此类推最后得到整个二维数组的情况为


012345
0000000
1012345
2013467
3013467
4013467

# 代码

Python :

 1a=input()
 2n, m=list(map(int, a.split()))
 3w=[0 for i in range(n + 1)]
 4v=[0 for i in range(n + 1)]
 5f=[0 for i in range(m + 1)]
 6for i in range(1, n + 1):
 7    b=input()
 8    w[i], v[i]=list(map(int, b.split()))
 9
10for i in range(1, n + 1):
11    for j in range(1, m + 1):
12        if j >= w[i]:
13            f[j]=max(f[j], f[j - w[i]] + v[i])
14            # 这个是考虑到可以选取多个物品的情况
15
16print(f[-1])

C++

 1int main(){
 2    int N,V;
 3    cin>>N>>V;
 4    vector<int> weight(N+1),value(N+1);
 5    for(int i=1;i<=N;i++)
 6        cin>>weight[i]>>value[i];
 7    vector<vector<int>> dp(N+1,vector<int>(V+1,0)); 
 8    for(int i=1;i<weight.size();i++){
 9        for(int j=0;j<=V;j++){
10
11            dp[i][j]=dp[i-1][j];
12
13            if(j-weight[i]>=0) 
14                dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
15        }
16    }
17    cout<<dp[N][V]<<endl;
18}
19

后记

2020大厂笔试 | 网易提前批(1) 

2020大厂笔试 | 网易提前批(2) 

令人头疼的背包九讲(1)0/1背包

数据结构类题目

具体算法类题目

  • 斐波那契数列

    • 007-斐波拉契数列

    • 008-跳台阶

    • 009-变态跳台阶

    • 010-矩形覆盖

  • 搜索算法

    • 001-二维数组查找

    • 006-旋转数组的最小数字(二分查找)

    • 037-数字在排序数组中出现的次数(二分查找)

  • 全排列

    • 027-字符串的排列

  • 动态规划

    • 030-连续子数组的最大和

    • 052-正则表达式匹配(我用的暴力)

  • 回溯

    • 065-矩阵中的路径(BFS)

    • 066-机器人的运动范围(DFS)

  • 排序

    • 035-数组中的逆序对(归并排序)

    • 029-最小的K个数(堆排序)

    • 029-最小的K个数(快速排序)

  • 位运算

  • 其他算法

    • 002-替换空格

    • 013-调整数组顺序使奇数位于偶数前面

    • 028-数组中出现次数超过一半的数字

    • 031-整数中1出现的次数(从1到n整数中1出现的次数)

    • 032-把数组排成最小的数

    • 033-丑数

    • 041-和为S的连续正数序列(滑动窗口思想)

    • 042-和为S的两个数字(双指针思想)

    • 043-左旋转字符串(矩阵翻转)

    • 046-孩子们的游戏-圆圈中最后剩下的数(约瑟夫环)

    • 051-构建乘积数组

剑指offer刷题交流群

  扫码添加微信,一定要备注研究方向+地点+学校+昵称(如机器学习+上海+上交+汤姆),只有备注正确才可以加群噢。

 ▲长按加群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值