您的位置:首页 > 其它

01 背包 完全背包 多重背包 三件套(这三个背包 想要吗?)

2017-08-08 16:44 190 查看
这几天 正在学习 动态规划 , 背包问题更是 必须学习 的内容 ,抽象难懂的 01 背包 问题 终于在我的 不懈努力下 ,把他给 攻克占领 啦 , 哈哈哈 , 后面的 小怪 完全背包 和 多重背包 更是不在话下!下面 根据一道题 来 分析一下 。

悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

急!灾区的食物依然短缺!

为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。

请问:你用有限的资金最多能采购多少公斤粮食呢?

后记:

人生是一个充满了变数的生命过程,天灾、人祸、病痛是我们生命历程中不可预知的威胁。

月有阴晴圆缺,人有旦夕祸福,未来对于我们而言是一个未知数。那么,我们要做的就应该是珍惜现在,感恩生活——

感谢父母,他们给予我们生命,抚养我们成人;

感谢老师,他们授给我们知识,教我们做人

感谢朋友,他们让我们感受到世界的温暖;

感谢对手,他们令我们不断进取、努力。

同样,我们也要感谢痛苦与艰辛带给我们的财富~

Input

输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。

Output

对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。

Sample Input

1

8 2

2 100 4

4 100 2

Sample Output

400

这道题 因为 数据的 范围都比较小 所以 多重背包 的 简单 01 方法 与 通
bef6
过二进制 优化 后的 01 背包 方法 都可 AC 。

先 简单说一下 前者 : 顾名思义 即使 完全套用 01 背包的方法 但 不同的 是 物品的数量 有多个 ,那么 也没 问题, 嘿嘿, 全部 都 便利一边不就行了 嘛, 是吧! 这里 提一下 : 01背包 的 方法中 有 用 一位数组 和 二维数组 两种 ,之所以 用后者 是因为 ,n种 物品 都只有一个 ,所以 弄了 一个 具有 n 行的 二维数组 ,每一种物品都分了 一行 , 是为了 避免 同一种物品 多次 放入 , 而 采用一位数组 是 采用 倒序 遍历 , 也是 同样的 道理 , 所以 完全背包 与 多重背包 同种 文品 有多个 就 不用采用 二维数组 啦 ,直接 用一位数组 倒序 遍历 , 比较简单 。 下面是完整代码(前一种方法)。(把第i种物品换成n[i]件01背包中的物品,则得到了物品数为∑n[i]的01背包问题,直接求解,复杂度仍然是O(V*∑n[i])。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int dp[110];
int value[210];
int weight[100];
int num[100];

int main()
{
int t; cin>>t;

while(t--)
{
int n, m;

scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &weight[i], &value[i], &num[i]);
memset(dp, 0, sizeof(dp));

for(int i = 1; i <= m; i++)
for(int j = 1; j <= num[i]; j++)
for(int k = n; k >= weight[i]; k--)
dp[k] = max(dp[k], dp[k-weight[i]] + value[i]);

printf("%d\n", dp
);
}

return 0;
}


看了上面的代码 之后 是不是觉得 这 太简单了把 ,是吧。我 但是 也是这么 觉得 的 但是 这 知识 动态规划 中 很简单 的 题;算是 入门题吧,所以呀 大家不要 觉得 啥都会啦 ,路还很长。

下面是 二进制 优化过的 方法 :但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。

方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,…,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。

分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,并不难,希望你自己思考尝试一下。

下面是 完整代码代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int dp[200];
int value[200];
int weight[100];
int num[100];

int main()
{
int t; cin>>t;

while(t--)
{
int n, m;

scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &weight[i], &value[i], &num[i]);
memset(dp, 0, sizeof(dp));

for(int i = 1; i <= m; i++)
{
int k = 1;
while(k < num[i])
{
for(int j = n; j >= k * weight[i]; j--)
dp[j] = max(dp[j], dp[j - k*weight[i]] + k*value[i]);
num[i] -= k;
k *= 2;
}

for(int j = n; j >= num[i]*weight[i]; j--)
dp[j] = max(dp[j], dp[j - num[i]*weight[i]] + num[i]*value[i]);
}

printf("%d\n", dp
);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: