背包问题???

来源:互联网 发布:用友软件联系方式 编辑:程序博客网 时间:2024/05/20 01:13

这星期水sdnuoj,有个人问我采药做没做,讲真的我是一点思路也没有。于是我就百度了一下,之后发现这是一种经典题型,背包问题。

我看了看背包问题有很多种 其中sdnuoj 1033是一道01背包问题,1043是一道完全背包问题,大概就是这样,我从百度文库和各位大佬那里学了一些,有点懂了,也不敢说全懂,讲一讲看法。



其中1033是这样的



辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 

如果你是辰辰,你能完成这个任务吗?

Input

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间(1 <= t <= T)和这株草药的价值(1 <= v <= 100000)。

Output

输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

Sample Input

100 577 9222 2229 8750 4699 90

Sample Output

133
#include <iostream>#include <algorithm>using namespace std;int t[1000];int v[1000];int f[1000];int main(){    int m,n,i,j;    cin>>n>>m;    for(i=1; i<=m; i++)    {        cin>>t[i]>>v[i];    }    for(i=1; i<=m; i++)        for(j=n; j>=1; j--)        {            if(t[i]<=j)                f[j]=max(f[j],f[j-t[i]]+v[i]);        }    cout<<f[n]<<endl;    return 0;}
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f[i][v]=max{ f[i-1][v], f[i-1][v-w[i]]+v[i] }。
可以压缩空间,f[v]=max{f[v],f[v-w[i]]+v[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,此时能获得的最大价值就是f [i-1][v-w[i]]再加上通过放入第i件物品获得的价值v[i]。
注意f[v]有意义当且仅当存在一个前i件物品的子集,其费用总和为f[v]。所以按照这个方程递推完毕后,最终的答案并不一定是f[N] [V],而是f[N][0..V]的最大值。如果将状态的定义中的“恰”字去掉,在转移方程中就要再加入一项f[v-1],这样就可以保证f[N] [V]就是最后的答案。至于为什么这样就可以,由你自己来体会了。
关于这个问题,有的大佬列出很多清晰明了的表格,非常感谢。
1043

Description

XXX上山去采药。XXX有一个容量为m(1<=m<=1000)的背包,他所采集的药材的总重量不能大于背包的容量。已知共有n(1<=n<=1000 )种药材,每种药材都有无限多,并且知道每种药材的重量w(1<=w<=m)及价值v(1<=v<=100000),如何选择,才能使得采到的药材的总价值最大?

Input

第1行为两个整数m和n,分别为背包的容量及药材的种数。 第2至n+1行每行两个整数w和v,分别表示每种药材的重量及价值。

Output

能采到的药材的最大总价值

Sample Input

100 577 9233 5034 6050 4699 161

Sample Output

161
#include <iostream>#include <algorithm>using namespace std;int t[10000];int v[10000];int f[10000];int main(){    int m,n,i,j;    cin>>n>>m;    for(i=1; i<=m; i++)    {        cin>>t[i]>>v[i];    }    for(i=1; i<=m; i++)        for(j=1; j<=n; j++)        {            if(t[i]<=j)                f[j]=max(f[j],f[j-t[i]]+v[i]);        }    cout<<f[n]<<endl;    return 0;}
这就是完全背包问题??和01背包问题一样,完全背包也可以用一维数组来保存数据。算法样式和01背包的很相似,唯一不同的是对V遍历时变为正序,而01背包为逆序。01背包中逆序是因为F[i][]只和F[i-1][]有关,且第i件的物品加入不会对F[i-1][]状态造成影响。而完全背包则考虑的是第i种物品的出现的问题,第i种物品一旦出现它势必应该对第i种物品还没出现的各状态造成影响。也就是说,原来没有第i种物品的情况下可能有一个最优解,现在第i种物品出现了,而它的加入有可能得到更优解,所以之前的状态需要进行改变,故需要正序。

原创粉丝点击