背包那些经典的例子( 上 )

来源:互联网 发布:中国教育现状数据分析 编辑:程序博客网 时间:2024/06/08 16:23
发现自己对背包问题还是情有独钟的嘛, 毕竟它也算的上是我真正学到的第一个算法。哎,现在我还是一个苦命挣扎的菜鸟。废话也不多说了,下面进入正题吧,这些都是菜鸟在刷背包题遇到的基础问题,作为菜鸟的我就是刷这些题一步一步理解背包的。

01背包问题——Bone Collector HDU - 2602
(我喜欢叫它基础背包,下面我都用基础背包叫它了)。
就不复制英文题目了,简单说一下意思,就是有个怪人,很喜欢收集骨头,然后有一堆不同的骨头,每个骨头有不同的价值和体积,你得用一个容量为V的背包尽可能地去装满一袋最大价值的骨头。
输入:
有T个案列,输入骨头数N,和袋子的容量V(N<=1000,V<=1000),然后输入一行骨头价值,一行体积。
至于思路问题好像真没什么说的,感觉就是套模板。
这个可以简化成基础背包问题,问题描述是这样的:

有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。

然后对比一下,发现其实就是名词变了而已,那么直接写出模板来。它的状态转移公式为f[i][j] = max( f[i-1][j], f[i-1][j-w[i]] + v[i] )。

对于初学的来说,这个还真难理解,作为菜鸟的我也花了两三天才真正弄懂这公式。不解的可以看看我的另一篇博客对基础背包的公式理解,不过这篇是当时理解灵感来的时候写好的,感觉没有写好,不懂再去翻翻大佬的博客去吧,毕竟我也是个菜鸟╮( •́ ₃•̀ )╭

#include <bits/stdc++.h>using namespace std;const int maxn=1010;//用结构体为了绑定每一件物品的价值与体积,直接用两个数组也可以。struct Pack{    int cost;    int val;};//f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。 用于状态转移的重要函数。int f[maxn][maxn];int main(){    int t, n, m;    pack a[maxn];    cin>>t;    while(t--)    {        memset(f, 0, sizeof(f));        cin>>n>>m;        for(int i=1;i<=n;i++)            cin>>a[i].val;        for(int i=1; i<=n;i++)            cin>>a[i].cost;    //两个循环可能看得有点蒙蔽,不过一般用到二维数组都是要两个循环的嘛,第一个是遍历每件物品用的,第二个j循环是表示当前物品放入一个j容量的背包。符合f[][]子问题定义嘛。        for(int i=1; i<=n; i++)        for(int j=0; j<=m;j++)        {            //有j-a[i].cost在,故而要判断一下,这个也可以不用判断,把j的范围改一下就可以,自己想一下吧。            if(j >= a[i].cost) f[i][j] = max(f[i-1][j], f[i-1][j-a[i].cost]+a[i].val);            else f[i][j] = f[i-1][j];        }//跟据子问题定义的f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。就能推出最大价值的状态为f[n][m],理解很容易,替换一下子母i,v就行了。        cout<<f[n][m]<<endl;    }    return 0;}

这只是一个最简单的,为了优化空间,我们得用到一维数组来写代码。
这个是基础背包模板代码:

#include <bits/stdc++.h>using namespace std;#define maxn 1010struct Pack{    int cost;    int weight;}a[maxn];int f[maxn], V;//核心的循环,而且通用,于是做成函数,以便直接调用了,说的这句话是后话, 不懂看我刚才说的那篇博客吧。void Basepack(int cost, int weight){    for(int v=V; v>=cost; v--)        f[v] = max(f[v], f[v-cost]+weight);}int main(){    int t, n;    cin>>t;    while(t--)    {        cin>>n>>V;        for(int i=1; i<=n; i++) cin>>a[i].weight;        for(int i=1; i<=n; i++) cin>>a[i].cost;        memset(f, 0, sizeof(f));        for(int i=1; i<=n; i++)            Basepack(a[i].cost, a[i].weight);        //f[i]代表着把东西装入容量为i的背包里的最大价值,然后自然就知道输出f[V]咯。        cout<<f[V]<<endl;    }    return 0;}

好了这是最基础的基础背包,下面就开始讲解完全背包吧。
完全背包问题——Piggy-Bank HDU - 1114
不想浪费我的时间与博客空间了,就直接写输入格式了,其他自己点开链接翻译吧。
输入:
有T个案列,钱罐的开始与最后的重量为E和F,下面有n中硬币,可以无限取,n行是硬币的价值与重量。

求钱罐里最少数目的钱。
我的代码直接用一维数组了,就是个完全背包的模板,没怎么去看二维的代码,因为用到三个循环真是太麻烦了!
基础背包公式:f[i][j] = max( f[i-1][j], f[i-1][j-w[i]] + v[i] )。
完全背包公式:f[i][j] = max(f[i-1][j], f[i - 1][j - k*w[i] ] + k*v[i] );
很类似吧!因为物品无限嘛,得有个k值一个一个取同种物品。
二维转一维同基础背包的一维公式一样
f[v] = max(f[v], f[v-cost]+weight)
数学思维看问题,不多说了,哎,真是恐怖的数学思维!
来吧看模板代码吧!

#include <bits/stdc++.h>using namespace std;#define maxn 10010//求最小值,则f的初始化不能为0了,而是为无穷的,当然程序里是没有无穷大的,就定一个很大的数了。#define INF 0x3f3f3f3fstruct Pack{    int cost;    int weight;}p[maxn];int f[maxn], V;//与上一个基础背包的模板很类似,因为完全背包每种物品无限个,于是乎是从一个开始取该物品到k个为止,然后呢也就是用它自身的前状态推出后一个状态,所以v是顺循环的。唔我的博客还没有这段讲解,以后补上吧。void Competepack(int cost, int weight){    for(int v=cost; v<=V; v++)        f[v] = min(f[v], f[v-cost]+weight);}int main(){    int t, E, F, n;    cin>>t;    while(t--)    {        cin>>E>>F;    //V相当于是背包的总容量        V = F-E;        memset(f, INF, sizeof(f));     //动态规划的初始化很微妙,稍微不注意就可能出错!f还得有一个为0,不然可以看        f[0] = 0;        cin>>n;        for(int i=1; i<=n; i++)        {            //很巧妙的输入操作,每次遍历都是访问i=1的状态值,不会影响i=2或则i=-1所以就直接在此处输入也行,因为他只会访问a[i]的值,但不访问a[i+1]或者其他的值。            cin>>p[i].weight>>p[i].cost;            Competepack(p[i].cost, p[i].weight);        }        //发现当容量为V的最小价值为INF呵呵呵,就不行了,INF定义为无穷大嘛,都无穷大的价值了,这不可能是最小价值的,所以是没有成立的条件了。        if(f[V] == INF)cout<<"This is impossible."<<endl;        else cout<<"The minimum amount of money in the piggy-bank is "<<f[V]<<"."<<endl;    }    return 0;}

完全背包这里还是没讲出感觉,哎不管了就这样吧,不懂的就敲几遍代码尝试理解吧!《背包九讲》真心不错,有空就看看,感觉为它打了很多次广告。。。。。。
博客写到这里太长了,就分两篇写吧,谔谔,也是算是偷懒了,下次把后面的补上,下一篇我来讲解多重背包 和 多重背包与完全背包的混合使用的两个简单而经典的题目。

哎,越到后面的背包,我讲得越简单模糊,毕竟是入门菜鸟初学没多久,望各位别介意哈......