01背包 完全背包 多重背包

好久没有更新博客了,这几天一直在学习WEB开发基础,忽略了算法的知识了,

今天我想讲一下01背包  完全背包  多重背包的知识,虽然网上一查一大把,我看了一下都认为不错

01背包是选择货物(也有其他类型,在这里我就用“货物”来代指了)重要的方法。。(指针对于每种货物只有一种)
01顾名思义,一种货物(也有可能被转化成其他,根据题意辨别)要么是0(就是不拿)要么是1(就是拿)。。
首先我们看一下背包的的本质:max1[j] = MAX(max1[j],max1[j-b[i].v]+b[i].w)意思就是你付出的代价得到的回报和你不付出代价的回报哪个更大就选择哪个(MAX是个函数表示在两个选项中选择一个最大的,max1[j]是你不付出代价的回报,max1[j-b[i].v]+b[i].w是你付出的代价
 得到回报b[i]),,这里就会有同学可能问了不付出代价怎么可能有回报呢?
 
 如果你有这个疑问你就是走入了一个误区,,,让咱们带着问题接着往 
下看,,,,
 
我们先来做一个事例看一下:
 

小P寻宝记——粗心的基友
Time Limit: 1000MS Memory limit: 65536K
题目描述
这对好基友他们在经历无数的艰难险阻后,终于找到了宝藏。无奈的是这一对好基友竟然是一样的粗心,又忘记了带一个大一点的包包,可惜啊、、选择又出现了啊、、
已知包的体积是v,每种宝贝只有一个,宝贝的体积是pi,价值是wi。求出这对粗心的基友可以最多带走价值多少的宝藏。
输入
输入数据有多组。
每组第一行有两个正整数n(n <= 10000)和v(v <= 10000)分别表示n种宝贝和包的体积。
接下来n行,每行有两个正整数vi, wi。
分别表示每种宝藏的体积vi (vi<=1000),价值wi(wi<=1000)。
输出
这对基友所能带走的最多的宝藏。
示例输入
5 10 
1 5 
2 4 
3 3 
4 2 
5 1
示例输出
14

代码如下

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

const int MAX = 10020;
struct Knapsack
{
    int v;
    int w;
}b[MAX];
int value[MAX];
int n, m;

int Knapsack_01 ( )
{
    int i, j;
    for ( i = 0;i < n; i++ )
    {
        for ( j = m;j >= b[i].v;j-- )
        {
            value[j] = max(value[j], value[j-b[i].v]+b[i].w);
        }
    }
    return value[m];
}

int main()
{
    int i;
    while ( ~scanf ( "%d %d", &n, &m ) )
    {
        for ( i = 0;i < n; i++ )
        {
            scanf ( "%d %d", &b[i].v, &b[i].w );
        }
        memset( value, 0, sizeof(value) );
        int sum = Knapsack_01( );
        printf ( "%d\n", sum );
    }
    return 0;
}

这里我们先做一个表格看一下这个代码跑的程序,,





有很多的刚接触01背包的并不能十分懂得01背包的作用,认为还不如直接贪心选择单位体积获得最大价值的方法依次放货物来的实在,,真的是这样吗??
下面我来编一组数来看看01背包和贪心的区别
输入
5 10
3 12
3 13
2 15
1 2
2 3
贪心会输出  42
01背包会输出 43
到底差在哪里呢?
哦原来贪心选了第四种物品,而01选了第五种物品啊!!自己想想为什么??


完全背包是指对于每种货物有若干种

而他的算法和01背包几乎可以说是别无而致,微微的一点区别时

将内循环的倒序改为正序

for (int i=0; i<n; i++)  
    for (int j=size[i]; j<=w; j++)  
        f[j] = max(f[j], f[j-size[i]]+value[i]);  
为什么之改变了内循环就可以使所得的结果大不相同呢?

这是因为正序使背包的空间可以被完全利用,小伙伴们可以自己模拟一下看看

/*

完全背包

V=10,N=3,c[]={3,4,5}, w={4,5,6}

(1)背包不一定装满

计算顺序是:从左往右,自上而下:  每个物品可以放多次,前面的会影响后面的

(2)背包刚好装满

计算顺序是:从左往右,自上而下。注意初始值,其中-inf表示负无穷

*/

多重背包是指一件物品可以有有限种数量

就比如说这个题

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

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15615    Accepted Submission(s): 6618


Problem Description
急!灾区的食物依然短缺!
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金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
 

Author
lcy
 我们可以把它认为是多重背包的基本题目
其做法也就是和01背包差不多一样,只是多加了一行罢了

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

int value[105];
struct beibao
{
    int c;
    int v;
    int b;
} a[105];

int main()
{
    int i, j, k;
    int t, m, n;
    scanf ( "%d", &t );
    while ( t-- )
    {
        scanf ( "%d %d", &n, &m );
        for(  i = 1; i <= m; i++ )
            scanf ( "%d %d %d", &a[i].c, &a[i].v, &a[i].b );
        memset(value, 0, sizeof(value) );
        for(  i = 1; i <= m; i++)
            for(  j = 1; j <= a[i].b; j++)
                for(  k = n; k >= a[i].c; k--)
                    value[k] = max(value[k], value[k-a[i].c]+a[i].v);
        printf ( "%d\n", value[n] );
    }
    return 0;
}
大家可以参考一下。

代码菜鸟,如有错误,请多包涵!!!
如有帮助记得支持我一下,谢谢!!!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值