背包问题,是动态规划问题中的典型的一类。顾名思义,是跟背包有关的问题(居然和名字有关,爷青结)。ios
大概就是讲背包空间有限,怎样合理地装物品可让总价值最高的问题。c++
其实也不是很是难。数组
背包问题的主要难点也就在于种类繁多,须要记忆各类递推公式、循环结构。优化
不过毕竟是记忆而已,老是比其余须要现场推倒递推公式的要好很多。atom
本文主要讲如下几类:spa
一个旅行者有一个最多能装 MM 公斤的背包,如今有 nn 件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn,求旅行者能得到最大总价值。code
第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);xml
第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每一个物品的重量和价值。blog
仅一行,一个数,表示最大总价值。it
10 4 2 1 3 3 4 5 7 9
12
既然要用用动态规划法解决0-1背包问题,咱们就先定义动态规划的三个要点,即状态、状态转移方程和边界条件。
首先咱们用子问题定义状态,咱们用F(i,j)表示“把前i件物品放入容量为j的背包中的最大总重量”。
而后咱们要考虑怎样的状态转移方程能够把这个问题转化为更小的子问题。咱们依然以“每个物品都有放或不放两种选择”的策略为基础,考虑第i件物品,若是咱们选择不放第i件物品,那么问题就直接转化为“把前i-1件物品放入容量为j的背包中的最大总重量”,若是咱们选择放第i件物品,那么问题就转化为“把前i-1件物品放入容量为j-V[i]的背包中的最大总重量加上第i件物品的重量”(这里值得注意的一点是,若是j-V[i]<0,即放入第i件物品后超过了背包容量的限制,那么咱们就只能选择不放第i件物品了)。因此状态转移方程为F(i,j)=max{F(i-1,j),F(i-1,j-V[i])+W[i]}。
边界条件则很容易获得,i=0时F(i,j)为0(没有物品就没有重量),j<0时F(i,j)为负无穷(但在代码中并不会这样初始化,咱们在j-V[i]<0时不计算第二种状况便可)。最终答案则是f(n,C)。
但其实这个空间复杂度是能够优化的。
能够直接用f[i]来表示当重量为i时,能够有的最大价值。
状态转移方程:
f[j] = max(f[j], f[j-c[i]]+w[i])
到了这里,终于要讲循环顺序的问题了。
其实第二层循环为何要反向的问题,我也研究了很久,终于在看一篇文章的时候恍然大悟。
根据题意,每种物品只有一件。
当咱们循环的时候,若是正序,那天然就会从前到后的更新数组。
那若是背包容量大于某件物品的多倍呢?
而刚好这件物品性价比很高?
假设a是一个常数。这件物品的质量为w。
那么,好比说,咱们在循环到i=a的时候,把这件物品装进了背包。
那再循环到i=a+w的时候,可能又会把这件物品装进背包。
也就是说,这件物品被使用了两次,甚至后面可能更多。
而根据题意,每件物品只有一个。
而对于每一次更新,只会用到i比当前小的数据,而不会用到i比当前大的。(由于要查看f[i-w[j]],w[j]不多是负的)
因此,先把大的更新了是没有问题的。
(学了背包问题这么久,终于把这个问题解决了!!!开心!!!)
#include <iostream> #include <cstdio> #define M 1000 using namespace std; int f[M], c[M], w[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d", &c[i], &w[i]); } for(int i = 1; i <= m; i++) { for(int j = v; j >= c[i]; j--) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("%d\n", f[v]); return 0; }
设有nn种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为MM,今从nn种物品中选取若干件(同一种物品能够屡次选取),使其重量的和小于等于MM,而价值的和为最大。
第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);
第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每一个物品的重量和价值。
仅一行,一个数,表示最大总价值。
10 4 2 1 3 3 4 5 7 9
max=12
那么,既然刚才讲了01背包由于每种物品只有一个因此只能逆序循环,彻底背包问题天然就是把循环顺序改成顺序就能够了!
——就这?就这?就这?
——对,就这。
——啊这,泪目
[doge]
#include <iostream> #include <cstdio> #define M 1000 using namespace std; int f[M], c[M], w[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d", &c[i], &w[i]); } for(int i = 1; i <= m; i++) { for(int j = c[i]; j <= v; j++) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("max=%d\n", f[v]); return 0; }
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。指望拨款金额能购买最大价值的奖品,能够补充他们的精力和体力。
第一行二个数n(n≤500),m(m≤6000),其中n表明但愿购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不一样的概念)和能购买的最大数量(买0件到s件都可),其中v≤100,w≤1000,s≤10。
一行:一个数,表示这次购买能得到的最大的价值(注意!不是价格)。
5 1000 80 20 4 40 50 9 30 50 7 40 30 6 20 20 1
1040
其实多重背包问题,虽然一个物品有了好几个,可是仍然能够按照01背包问题的思路,每一个物品能取几件就存储几回就行了(当作不一样的物品)
#include <bits/stdc++.h> #define M 1000 using namespace std; int f[M], c[M], w[M],num[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &c[i], &w[i],&num[i]); } int jjc=m; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= num[i]; ++j) { c[jjc]=c[i]; w[jjc]=w[i]; jjc++; } } for(int i = 1; i <= m; i++) { for(int j = c[i]; j <= v; j++) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("max=%d\n", f[v]); return 0; }
宠物小精灵是一部讲述小智和他的搭档皮卡丘一块儿冒险的故事。
一天,小智和皮卡丘来到了小精灵狩猎场,里面有不少珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每个野生小精灵而言,小智可能须要使用不少个精灵球才能收服它,而在收服过程当中,野生小精灵也会对皮卡丘形成必定的伤害(从而减小皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(由于他须要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。
咱们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。若是小智选择了收服,那么必定会扔出可以收服该小精灵的精灵球,而皮卡丘也必定会受到相应的伤害;若是选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。
小智的目标有两个:主要目标是收服尽量多的野生小精灵;若是能够收服的小精灵数量同样,小智但愿皮卡丘受到的伤害越小(剩余体力越大),由于他们还要继续冒险。
如今已知小智的精灵球数量和皮卡丘的初始体力,已知每个小精灵须要的用于收服的精灵球数目和它在被收服过程当中会对皮卡丘形成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?
输入数据的第一行包含三个整数:N(0<N<1000),M(0<M<500),K(0<K<100),分别表明小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。
以后的K行,每一行表明一个野生小精灵,包括两个整数:收服该小精灵须要的精灵球的数量,以及收服过程当中对皮卡丘形成的伤害。
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。
10 100 5 7 10 2 40 2 50 1 20 4 20
3 30
10 100 5 8 110 12 10 20 10 5 200 1 110
0 100
对于样例输入2:小智一个小精灵都无法收服,皮卡丘也不会收到任何伤害,因此输出0 100。
一个旅行者有一个最多能装V公斤的背包,如今有n件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,且价值总和最大。
第一行:三个整数,V(背包容量,V≤200),N(物品数量,N≤30)和T(最大组号,T≤10);
第2..N+1行:每行三个整数Wi,Ci,PWi,Ci,P,表示每一个物品的重量,价值,所属组号。
仅一行,一个数,表示最大总价值。
10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3
20