写在前面:

自从学了背包这个专题,感觉永远也写不完,之前写了三种类型的01背包,结果现在发现都是简单的01背包、后来又学了多重背包、完全背包,其中多重背包还可以进行二进制优化。

本文将对这几种常见的背包进行解释

另外,找到了一套OJ上比较不错的01背包专题,不是简单的签到题,但是也不是特别难

https://vjudge.net/contest/296080#problem/F

模块一:简单01背包

首先,如果你不知道什么是01背包,请先参考博客,补充相关知识

https://blog.csdn.net/xp731574722/article/details/70766804

那么,对于简单01背包,一共有三种写法,dp的分别有两种,dfs+剪枝的一种,分别对应不同的题目。

①普通写法,也是最简单的写法:

例题是HDU 2602

Bone Collector

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14336    Accepted Submission(s): 5688

Problem Description

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

Output

One integer per line representing the maximum of the total value (this number will be less than 231).

Sample Input

1

5 10

1 2 3 4 5

5 4 3 2 1

Sample Output

14

Source

HDU 1st “Vegetable-Birds Cup” Programming Open Contest

如果是才接触背包,请认真写题,然后理解

这里也简单说一下吧,普通的01背包,就是用 dp[vol]=value 表示vol体积这个状态下最大的价值value,然后不断更新,更新有两层for循环, 第一层是for循环1-n个物品,第二层是for循环1-bag_volume,循环背包的体积,当然这里第二层可以优化的写的,一般我们是for(背包的最大体积--该物体的体积),写好两层循环,就是状态转移dp[j] = max( dp[j],dp[j-vol]+val );  (vol表示当前物体体积,val表示当前物体价值,另外这里用的是空间优化的)

复杂度是 O(n*vol )n是物品个数,vol是背包容量。

第一种写法适用于n和vol都一般大( n是物体个数,vol是bag最大体积 ),差不多在1000,两者的乘积在可承受范围内(1e7)。

普通的我就不详细解释了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n,vol;
struct spe
{int value,vol;
};
spe p[1005];
int dp[1005];
int main()
{int T;scanf("%d",&T);while( T-- ){scanf("%d %d",&n,&vol);for( int i = 1 ; i <= n; i++ ){scanf("%d",&p[i].value);}for( int i = 1 ; i <= n; i++ ){scanf("%d",&p[i].vol);}memset(dp,0,sizeof dp);for( int i = 1 ; i <= n ; i++ ){for( int j = vol ; j >= p[i].vol ; j-- ){dp[j] = max( dp[j],dp[j-p[i].vol]+p[i].value );}}cout<<dp[vol]<<endl;}return 0;
}
/*1 2
1 7
1 3
1 4
1 9
3*/

②第二种写法,按照价值来进行dp:

当然,它也有它的使用范围拉,当价值的范围很小的时候才能用这个,而且用这个算法,它的复杂度和背包容量没有关系,所以说我们是根据价值来写的。

算法复杂度是O(n*m) 其中m是背包价值的总和,所以很显然,背包价值范围很小,就用这个。

其中dp[i] = k,表示的是,已经装了价值为 i 的物品所需要的最小体积k。

类似的题有HDU 2955,但这道题掺杂了一点概率。

完全类似的是牛客网上的一道题:

https://www.nowcoder.com/acm/contest/119/F

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define ll long long
using namespace std;
ll dp[10000005];
ll val[105],v[105];
ll T,n,m;int main()
{scanf("%lld",&T);while(T--){scanf("%lld%lld",&n,&m);int sum = 0;for(int i=0;i<n;i++){scanf("%lld%lld",&v[i],&val[i]);sum += val[i];}memset(dp,0x3f3f3f3f,sizeof(dp));dp[0] = 0;        //一定不能省略   for(int i=0;i<n;i++)for(int j=sum;j>=val[i];j--)dp[j] = min(dp[j], dp[j - val[i]] + v[i]);// int ans = -1;for(int i=sum;i>=0;i--)//最后还需要遍历一遍,找出体积符合条件的最大价值{if(dp[i] <= m){printf("%d\n",i);break;}}}return 0;
}

③第三种也就是DFS+剪枝了,很少见到,但是最近做题还是遇到了。

试用于物品个数比较少,但是背包容量和物品价值都太大了,这时候显然用不成前两种方法。

关于这个有一个例题和解析。

https://blog.csdn.net/feynman1999/article/details/71123241

这个例题好像访问不了,还有一个HDU 3448(但是感觉数据有点水) https://vjudge.net/contest/296080#problem/F

然后我做的题目是计蒜客上面的

https://nanti.jisuanke.com/t/29374

解释见注释

#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
typedef long long ll;
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define fork(l,r) for( int k = 1 ; k <= r ; k++ )
#define mem(a,val) memset(a,val,sizeof a)
#define lef rt<<1
#define rig rt<<1|1
#define mid (l+r)>>1
using namespace std;const int maxn = 110;
int n;
ll vol; //背包的体积struct spe
{ll vol,val; //物品所需要的体积和价值
};
spe p[maxn];
spe backsum[maxn];
ll ans;
void dfs( int th,ll val,ll volume ) //三个参数: 遍历到第几个物品, 当前价值和 、体积和
{ans = max(ans,val);for( int i = th+1 ; i <= n ; i++ ){if( volume+backsum[i].vol <= vol )  //如果后面直接装的下  ,剪枝①{ans = max(ans,val+backsum[i].val);return;}if( p[i].vol+volume <= vol && val+backsum[i].val > ans )    //如果当前装的下,而且后面的加起来比ans大,说明后面的潜力大dfs(i,val+p[i].val,volume+p[i].vol );                       //if的第二个条件是剪枝②}
}
bool cmp( spe a,spe b )
{return a.vol > b.vol;
}
int main()
{while( scanf("%d %lld",&n,&vol) == 2 ){mem(backsum,0);ans = 0;fori(1,n)scanf("%lld %lld",&p[i].vol,&p[i].val);sort(p+1,p+1+n,cmp);backsum[n].val = p[n].val;backsum[n].vol = p[n].vol;for( int i = n-1 ; i >= 1 ; i-- ) //求后缀和,为剪枝做准备{backsum[i].val = p[i].val+backsum[i+1].val;backsum[i].vol = p[i].vol+backsum[i+1].vol;}dfs(0,0,0);printf("%lld\n",ans);}return 0;
}
/**/

HDU 3448代码(本人觉得数据水)

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<set>using namespace std;#define mem(a,val) memset(a,val,sizeof a)
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define fork(l,r) for( int k = l ; k <= r ; k++ )
#define inf 0x3f3f3f3f
#define lef rt<<1
#define rig rt<<1|1
#define mid (l+r)>>1
typedef long ll;
#define see(info,val) cout<<info<<" "<<val<<endl;int n,kind;
ll vol;
ll a[105];
ll backsum[105];
ll ans;
bool cmp( ll a,ll b )
{return a>b;
}void dfs( int start,int cnt,ll total )
{if( total > vol || cnt > n )return;if( total+backsum[start] <= ans )return;if( cnt+(kind-start+1) <= n && total+backsum[start] <= vol )    //如果直接装的下{total+= backsum[start];ans = max(ans,total);return;}ans = max(ans,total);for( int i = start ; i <= kind ; i++ ){// v.push_back(a[i]);dfs(i+1,cnt+1,total+a[i]);// v.pop_back();}
}
int main()
{while( cin>>n>>vol ){mem(backsum,0);ans = 0;scanf("%d",&kind);fori(1,kind){cin>>a[i];}//   scanf("%lld",&a[i]);sort(a+1,a+1+kind,cmp);for( int i = kind ; i >= 1 ; i-- )backsum[i] = backsum[i+1]+a[i];if( backsum[1]-backsum[n+1] <= vol )    //这个特判对于这个题就是个bug, 数据太水ans = backsum[1]-backsum[n+1];else dfs(1,0,0);//printf("%lld\n",ans);cout<<ans<<endl;}return 0;
}
/*
2 24
6
1 6 9 12 13 172 20
6
1 6 9 12 13 172 21
6
1 6 9 12 13 172 22
6
1 6 9 12 13 17*/

那么,以上就是三种普通的01背包,如果遇到,差不多就直接上模板就行了。

模块二:多重背包、完全背包

完全背包:

完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

完全背包按其思路仍然可以用一个二维数组来写出:

f[i][v]=max{ f[i-1][v-k*c[i]]+k*w[i] }  其中0<=k*c[i]<=v

关于完全背包的,感觉自己没别人讲的好,只能推荐一下别人的文章了

https://www.cnblogs.com/Kalix/p/7622102.html

多重背包

多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:

f[i][v]=max{ f[i-1][v-k*c[i]]+k*w[i] }   其中0<=k<=n[i]

通俗的将,完全背包就是物品有无限个,而多重背包物品有很多个,但不是无限个。

它们与普通的01背包的区别在于一种物品有很多个,而普通的01背包一个物品只有一件

对于多重背包,我们还可以进行二进制优化(个人建议学了多重背包就把这个看一下,其实很简单)。

那么什么是二进制优化呢?

举个例子。假如给了我们 价值为 2,但是数量却是10 的物品,我们应该把10给拆开,要知道二进制可是能够表示任何数的,所以10 就是可以有1,2, 4,8之内的数把它组成,一开始我们选上 1了,然后让10-1=9,再选上2,9-2=7,在选上 4,7-4=3,

而这时的3<8了,所以我们就是可以得出 10由 1,2,4,3,来组成,就是这个数量为1,2,3,4的物品了,那么他们的价值是什么呢,是2,4,6,8,也就说给我们的价值为2,数量是10的这批货物,已经转化成了价值分别是2,4,6,8元的货物了,每种只有一件哎!!!!这就是二进制优化的思想。

那为什么会有完全背包和01 背包的不同使用加判断呢?原因也很简单啊,当数据很大,大于背包的容纳量时,我们就是在这个物品中取上几件就是了,取得量时不知道的,也就理解为无限的啦,这就是完全背包啦,反而小于容纳量的就是转化为01背包来处理就是了,可以大量的省时间。

关于二进制分解的模板如下,我也用的这个,如果对于我的讲解不懂的,也可以看看这个博客

https://blog.csdn.net/bentutut/article/details/77855318

下面是例题:

HDU 2844

很好的一道题啊,结合完全背包和多重背包

题目的意思:
  第一行输入,n,m分别表示n种硬币,m表示总钱数。
  第二行输入n个硬币的价值,和n个硬币的数量。
  输出这些硬币能表示的所有在m之内的硬币种数。

我是将他们分解成完全背包和多重背包来做的,因为一些物品如果太多,那么就可以把他们当成无数个,剩下的当成多重背包,这样时间应该更短一些。

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.

Input

The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

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

Sample Output

8
4
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#include<time.h>
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define fork(l,r) for( int k = l ; k <= r ; k++ )
#define mem(a,val) memset(a,val,sizeof a)
#define lef rt<<1
#define rig rt<<1|1
#define mid (l+r)>>1
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
int n,m;
struct spe
{int val,number;
};
const int maxn = 1e5+5;
spe p[maxn];
int multi_bag[maxn];
int complete_bag[maxn];
int multicnt,completecnt;
bool vis[maxn];
int main()
{while( scanf("%d %d",&n,&m) ==2 ){if( n == 0 && m == 0 )break;mem(vis,false);vis[0] = true;multicnt = 1;completecnt = 1;fori(1,n)scanf("%d",&p[i].val);fori(1,n)scanf("%d",&p[i].number);fori(1,n)if( p[i].val*p[i].number >= m )        //放入完全背包处理complete_bag[completecnt++] = i;else                                    //放入多重背包{for( int j = 1 ; j <= p[i].number ; j <<=1 ){multi_bag[multicnt++] = p[i].val*j;    //进行二进制分解p[i].number -= j;}if( p[i].number > 0 )multi_bag[multicnt++] = p[i].val*p[i].number;}int pos;for( int i = 1 ; i < completecnt ; i++ )    //完全背包{pos = complete_bag[i];for( int j = p[pos].val ; j <= m ; j++ )if( vis[ j-p[pos].val ] )vis[j] = true;}for( int i = 1 ; i < multicnt ; i++ )        //多重背包{for( int j = m ; j >= multi_bag[i] ; j-- )if( vis[ j-multi_bag[i] ] )vis[j] = true;}int ans = 0;fori(1,m)if( vis[i] )ans++;printf("%d\n",ans);}return 0;
}/*1000
3 100
1 2
3 1
4 2*/

后续不断更新中

最后给自己打个广告吧,自己做了一个网站,大家可以访问访问

https://www.bowenyang666.com

01背包总结+传授个人经验相关推荐

  1. 【每日DP】day1 P1802 5倍经验日(别样的01背包)难度⭐★

    题目链接 输入 6 8 21 52 1 21 70 5 21 48 2 14 38 3 14 36 1 14 36 2 输出 1060 一道有点意思的01背包,可以帮助理解.好久没写DP了,每天一道D ...

  2. hdu 2955 01背包

    http://acm.hdu.edu.cn/showproblem.php?pid=2955 如果认为:1-P是背包的容量,n是物品的个数,sum是所有物品的总价值,条件就是装入背包的物品的体积和不能 ...

  3. 算法模板:动态规划之01背包【沈七】

    算法模板:动态规划之01背包 前言 动态规划 01背包 二维背包 一维优化 经典习题 小A点菜 5 倍经验日 买干草 完结散花 参考文献 前言 唤我沈七就好啦. 动态规划 核心作用:优化 当数据范围& ...

  4. 浅说——九讲背包之01背包

    所谓九讲,也就是: 0/1背包 0/1背包降维 完全背包 多重背包(二进制优化) 混合背包 二维费用背包 分组背包 有依赖的背包 背包的方案总数\背包的具体方案路径 0/1背包: [问题描述](经典) ...

  5. Codeforces Round #104 (Div. 2) E DP(01背包模型) +组和+除法取模求逆元

    题意: 规定只包含4或7的数为幸运数字,给定n个数的序列,求他的子序列,使得该子序列的长度为k并且满足该子序列中不存在相同的两个幸运数字.问一共寻在多少种可能.(只要该数的下标不同则认为是不同的序列) ...

  6. 最小邮票数(01背包)

    题目描述 有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值.     如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分.3分.4分即可. 输入描述: 有多组数 ...

  7. NYOJ 860 又见01背包

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

  8. 背包问题(多重背包+0-1背包)

    一:0-1背包问题 #include<iostream> #include<algorithm> #include<cstring> const int maxn= ...

  9. 背包模型dp1:01背包,完全背包,多重背包的两大优化的详解

    01背包问题: 状态表示:f[i][j]表示从只从前i个物体里面选,切总体积不超过j的选法的集合状态表示:f[i][j]表示从只从前i个物体里面选,切总体积不超过j的选法的集合状态表示:f[i][j] ...

最新文章

  1. 异形隔离java剧情_异形隔离攻略 系统上手教程 全剧情流程图文攻略(41)
  2. LeetCode 59 Spiral Matrix II(螺旋矩阵II)(Array)
  3. 从SQL Server 2000/2005到SQL Server 2008的升级测试
  4. 透過 OpenNI 建立 Kinect 3D Point Cloud
  5. TensorFlow for Hackers (Part VI) - Human Activity Recognition using LSTMs on Android
  6. sqlmap 相关参数
  7. Spring--SPeL
  8. C++ delete只能用来释放new分配的内存
  9. go新手看的开源项目 哪些适合_最近大家都在用 Go 语言玩什么?这几个新的开源项目告诉你...
  10. hbuilderx内置服务器启动失败_Nginx服务器简介与配置
  11. PSENet PANNet DBNet 三个文本检测算法异同
  12. python实现:用类实现一个图书馆,实现借书,入库,还书,查书,等功能,要求数据可以保存到文件中,退出后下次可以找回数据...
  13. python飞机大战类_python微信飞机大战
  14. Intel(R) Matrix Storage Manager 介绍
  15. react引入antd报错找不到antd/dist/antd.css Module not found: Error: Can‘t resolve ‘antd/dist/antd.css‘ in
  16. 【数据库考试】考研复试必备数据库试题
  17. python学习笔记 Counter()
  18. MiniGUI——第一个程序helloworld
  19. 微信小程序自动注册获取token
  20. 喵星球上的点名——记一个用广义SAM根号维护多串的技巧

热门文章

  1. linux chown sh,chown命令示例
  2. 2020-9-20 周报
  3. 项目管理学习总结(15)——技术负责人所需的四个核心能力
  4. 利用上虚拟化,说一下TLB,以及VHE
  5. 仿知音漫画网站源码 PC+手机端 帝国cms7.5内核
  6. 1.2.1数据,数据元素,数据项和数据对象
  7. 绿色智慧档案馆构想之智慧档案馆环境综合管控一体化平台
  8. cocoscreator初学笔记001
  9. 罗升阳对安卓2.3系统的总结
  10. usrp b210 参数记录