令人头疼的背包九讲(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 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 0 0 0 0
2 0 0 0 0 0 0
3 0 0 0 0 0 0
4 0 0 0 0 0 0

由输入可知第一个物品为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)

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


0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 1 2 3 4 5
2 0 0 0 0 0 0
3 0 0 0 0 0 0
4 0 0 0 0 0 0

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)

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


0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 1 2 3 4 5
2 0 1 3 4 6 7
3 0 0 0 0 0 0
4 0 0 0 0 0 0

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


0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 1 2 3 4 5
2 0 1 3 4 6 7
3 0 1 3 4 6 7
4 0 1 3 4 6 7

# 代码

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 weight(N+1),value(N+1);
 5    for(int i=1;i<=N;i++)
 6        cin>>weight[i]>>value[i];
 7    vector> dp(N+1,vector(V+1,0)); 
 8    for(int i=1;i=0) 
14                dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
15        }
16    }
17    cout<

后记

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

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

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

数据结构类题目

  • LinkedList

    • 003-从尾到头打印链表

    • 014-链表中倒数第k个结点

    • 015-反转链表

    • 016-合并两个或k个有序链表

    • 025-复杂链表的复制

    • 036-两个链表的第一个公共结点

    • 055-链表中环的入口结点

    • 056-删除链表中重复的结点

  • Tree

    • 004-重建二叉树

    • 017-树的子结构

    • 018-二叉树的镜像

    • 022-从上往下打印二叉树

    • 023-二叉搜索树的后序遍历序列

    • 024-二叉树中和为某一值的路径

    • 026-二叉搜索树与双向链表

    • 038-二叉树的深度

    • 039-平衡二叉树

    • 057-二叉树的下一个结点

    • 058-对称的二叉树

    • 059-按之字形顺序打印二叉树

    • 060-把二叉树打印成多行

    • 061-序列化二叉树

    • 062-二叉搜索树的第k个结点

  • Stack & Queue

    • 005-用两个栈实现队列

    • 020-包含min函数的栈

    • 021-栈的压入、弹出序列

    • 044-翻转单词顺序列(栈)

    • 064-滑动窗口的最大值(双端队列)

  • Heap

    • 029-最小的K个数

  • Hash Table

    • 034-第一个只出现一次的字符

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

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

具体算法类题目

  • 斐波那契数列

    • 007-斐波拉契数列

    • 008-跳台阶

    • 009-变态跳台阶

    • 010-矩形覆盖

  • 搜索算法

    • 001-二维数组查找

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

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

  • 全排列

    • 027-字符串的排列

  • 动态规划

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

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

  • 回溯

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

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

  • 排序

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

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

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

  • 位运算

    • 011-二进制中1的个数

    • 012-数值的整数次方

    • 040-数组中只出现一次的数字

  • 其他算法

    • 002-替换空格

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

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

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

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

    • 033-丑数

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

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

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

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

    • 051-构建乘积数组

剑指offer刷题交流群

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

 ▲长按加群

你可能感兴趣的:(令人头疼的背包九讲(2)完全背包问题)