目录
初学背包问题,综合了很多博客的文章,总结了一下知识点和基础的习题。
知识点
一、01背包
有n件物品(每种物品都只有一件),
w[i]表示物品的重量,v[i]表示物品的价值,
现有一个容量为V的背包,
应该如何选物品使得书包内装的物品的value之和最大呢?
例:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,每件物品数量只有一个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
迭代数组:
name weight value 0 1 2 3 4 5 6 7 8 9 10 a 2 6 0 6 6 6 6 6 6 6 6 6 6 b 2 3 0 6 6 9 9 9 9 9 9 9 9 c 6 5 0 0 0 0 0 0 9 9 11 11 14 d 5 4 0 0 0 0 0 4 9 9 11 11 14 e 4 6 0 0 0 0 6 6 9 9 11 11 15
核心代码:
对于第 个物品,有选和不选两种情况
表示在容量为
的情况下选取
个物品的最大
value 值。
核心代码:
for (i = 1; i <= n; i++)
for (j = w[i]; j <= V; j++)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
表示 背包容量 为 j 时,能够包容的最大 value 值。
核心代码:
for (i = 1; i <= n; i++)
for (j = V; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
学习博客:
01背包问题 二维解法 及 输出路径:https://blog.youkuaiyun.com/weixin_40372617/article/details/102872786
一维01背包:为什么可行?为什么逆序?https://blog.youkuaiyun.com/weixin_40372617/article/details/102872786
https://blog.youkuaiyun.com/Cassie_zkq/article/details/82944880
二、完全背包
有n件物品(每种物品有无数件),
w[i]表示物品的重量,v[i]表示物品的价值,
现有一个容量为V的背包,
应该如何选物品使得书包内装的物品的value之和最大呢?
例:有编号分别为a,b,c,d的四件物品,它们的重量分别是2,3,4,7,它们的价值分别是1,3,5,9,每件物品数量无限个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
迭代数组:
name weight value 0 1 2 3 4 5 6 7 8 9 10 a 2 1 0 0 1 1 2 2 3 3 4 4 5 b 3 3 0 0 1 3 3 4 6 6 7 9 9 c 4 5 0 0 1 3 5 5 6 8 10 10 11 d 7 9 0 0 1 3 5 5 6 9 10 10 12
核心代码:
for (i = 1; i <= n; i++)
for (j = w[i]; j <= V; j++)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
学习博客:动态规划之背包问题(二):完全背包问题:https://blog.youkuaiyun.com/siyu1993/article/details/52858940
背包问题详解:01背包、完全背包、多重背包:https://blog.youkuaiyun.com/na_beginning/article/details/62884939
三、多重背包
有n件物品(每种物品有 num[i] 件),
w[i]表示物品的重量,v[i]表示物品的价值,
现有一个容量为V的背包,
应该如何选物品使得书包内装的物品的value之和最大呢?
例:有编号分别为a,b,c的三件物品,它们的重量分别是1,2,2,它们的价值分别是6,10,20,他们的数目分别是10,5,2,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和?
解题思路:
作为一个新问题考虑,由于每个物品多了数目限制,因此初始化和递推公式都需要更改一下。初始化时,只考虑一件物品a时,f[1][j] = min{num[1], j/weight[1]}。 计算考虑i件物品承重限制为y时最大价值f[i][y]时,递推公式考虑两种情况,要么第 i 件物品一件也不放,就是f[i-1][y], 要么第 i 件物品放 k 件,其中 1 <= k <= (y/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,即f[i][y] = max{f[i-1][y], (f[i-1][y-k*weight[i]]+k*value[i])}。 这里为什么不能像完全背包一样直接考虑f[i][y-weight[i]]+value[i]呢?因为这样不容易判断第 i 件物品的个数是否超过限制数量 num[i]。
name | weight | value | num | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
a | 1 | 6 | 10 | 0 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 |
b | 2 | 10 | 5 | 0 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 |
c | 2 | 20 | 2 | 0 | 6 | 20 | 26 | 40 | 46 | 52 | 58 | 64 |
习题
一:01背包
1、装箱问题
模板题,难度:★☆☆☆☆
时间限制 : 1.000 sec 内存限制 : 128 MB
【问题描述】
有一个箱子的容量为V(V为正整数,且满足0≤V≤20000),同时有n件物品(0的体积值为正整数。
要求从n件物品中,选取若干装入箱内,使箱子的剩余空间最小。
输入:1行整数,第1个数表示箱子的容量,第2个数表示有n件物品,后面n个数分别表示这n件
物品各自的体积。
输出:1个整数,表示箱子剩余空间。
【输入输出样例】
输入:24 6 8 3 12 7 9 7
输出:
0
#include <bits/stdc++.h>
using namespace std;
int main()
{
int v, n, a[200005], dp[200005];
cin >> v >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) //遍历物品
for (int j = v; j >= a[i]; j--) //倒序遍历箱子容量
dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
int ans=v-dp[v];
cout<<ans<<endl;
return 0;
}
2、采药
模板题,难度:★☆☆☆☆
时间限制 : 1.000 sec 内存限制 : 128 MB
【题目描述】
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。
医师为了判断他的资质,给他出了一个难题。
医 师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间, 在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
【输入 】
第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,
T代表总共能够用来采药的时间,M代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的整 数,分别表示采摘某株草药的时间和这株草药的价值。
【输出】
一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
【样例输入】
70 3 71 100 69 1 1 2
【样例输出】
3
【数据规模】
对于30%的数据,M <= 10;
对于全部的数据,M <= 100。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main()
{
int t, m, time[1005], value[1005], dp[1005] = {0};
cin >> t >> m;
for (int i = 1; i <= m; i++)
cin >> time[i] >> value[i];
for (int i = 1; i <= m; i++)
for (int j = t; j >= time[i]; j--)
dp[j] = max(dp[j], dp[j - time[i]] + value[i]);
cout << dp[t] << endl;
return 0;
}
3、hdu - 2602 - Bone Collector
模板题,难度:★☆☆☆☆
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem DescriptionMany 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 ?
InputThe 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.OutputOne integer per line representing the maximum of the total value (this number will be less than 231).Sample Input1 5 10 1 2 3 4 5 5 4 3 2 1
Sample Output
14
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T, n, v;
int volume[1005], value[1005], dp[1005];
cin >> T;
while (T--)
{
memset(dp, 0, sizeof(dp));
cin >> n >> v;
for (int i = 1; i <= n; i++)
cin >> value[i];
for (int i = 1; i <= n; i++)
cin >> volume[i];
for (int i = 1; i <= n; i++)
{
for (int j = v; j >= volume[i]; j--)
{
dp[j] = max(dp[j - 1], dp[j - volume[i]] + value[i]);
}
}
cout << dp[v] << endl;
}
//system("pause");
return 0;
}
4、poj - 3624 - Charm Bracelet
模板题,难度:★☆☆☆☆
Time Limit: 1000MS Memory Limit: 65536K
【Description】
Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a 'desirability' factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).
Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.
【Input】
* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di【Output】
* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints
Sample Input
4 6 1 4 2 6 3 12 2 7
Sample Output
23
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int T, n, m;
int w[20005], d[20005], dp[20005];
while (cin >> n >> m)
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
cin >> w[i] >> d[i];
for (int i = 1; i <= n; i++)
for (int j = m; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + d[i]);
cout << dp[m] << endl;
}
//system("pause");
return 0;
}
5、hdu - 2564 - 饭卡
难度:★★☆☆☆
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。Input多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。Output对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。Sample Input1 50 5 10 1 2 3 2 1 1 2 3 2 1 50 0
Sample Output
-45 32
题目思路:
将该问题转化成0-1背包问题,由于每个菜只能购买一次,而且每次买菜的时候饭卡余额 m 要大于等于5,因此先利用0-1背包算出 m - 5 的钱最多能买多少菜,然后拿最后的5块钱买最贵的菜,才能使饭卡余额达到最少。而且这道题还要注意的一点是在进行0-1背包之前要将所有的菜从小到大排一下序,因为要使饭卡余额最少必须要用最后5块钱买最贵的菜,而每个菜只能购买一次,因此要在0-1背包之前sort排序,防止最贵的菜购买了多次。
学习于:https://blog.youkuaiyun.com/qq_41181772/article/details/88650132
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main()
{
int n, balance, value[1005], dp[1005];
while (cin >> n, n)
{
for (int i = 1; i <= n; i++)
cin >> value[i];
cin >> balance;
sort(value + 1, value + 1 + n);
memset(dp, 0, sizeof(dp));
if (balance < 5)
{
cout << balance << endl;
continue;
}
for (int i = 1; i < n; i++)
for (int j = balance - 5; j >= value[i]; j--)
dp[j] = max(dp[j], dp[j - value[i]] + value[i]);
cout << balance - dp[balance - 5] - value[n] << endl;
}
return 0;
}
二、完全背包
第一题:HDU- 1114 - Piggy-Bank
完全背包的模板题,难度:★★☆☆☆
【题意】
首先第一行输入测试次数,然后每个测试用例第一行:空存钱罐的重量 和 装满钱时候存钱罐的重量,然后第二行输入现有的钱的种类n,后面就是每种钱的价值和钱的重量。要求用存钱罐中钱的重量来找到存钱罐中的最低金额,如果可以找到那么打印“ The minimum amount of money in the piggy-bank is X.”,其中X是使用给定总重量的硬币可以达到的最低金额。如果不能准确地达到重量,This is impossible.”。
【样例输入】
3 10 110 2 1 1 30 50 10 110 2 1 1 50 30 1 6 2 10 3 20 4
【样例输出】
The minimum amount of money in the piggy-bank is 60. The minimum amount of money in the piggy-bank is 100. This is impossible.
参考博客:https://blog.youkuaiyun.com/yc_cy1999/article/details/105129923
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(false);
int T, e, f, n, p[505], w[505], dp[10005];
cin >> T;
while (T--)
{
memset(dp, inf, sizeof(dp));
dp[0] = 0;
cin >> e >> f;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> p[i] >> w[i];
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= f - e; j++)
dp[j] = min(dp[j], dp[j - w[i]] + p[i]);
if (dp[f - e] == inf)
cout << "This is impossible.\n";
else
cout << "The minimum amount of money in the piggy-bank is " << dp[f - e] << ".\n";
}
//system("pause");
return 0;
}