背包问题是动态规划最基础的基础,这里本蒟蒻讲解一下几类背包问题。
有N件物品和一个容量为V的背包,每种物品只有一个,放入第i件物品的费用是Ci,价值是Wi。
求将若干个物品装入背包得到的最大价值。(总费用不能超过总容量V)
最基本的背包,每种物品只有一个,可放可不放。
很容易可以定义出状态F[i][j]表示前i件物品放入一个容量为j的背包的最大价值。
状态转移方程是F[i][j]=max(F[i-1][j],F[i-1][v-Ci]+Wi)(即不放与放)。
所以可以得到代码:
//F数组初值为0 for(int i=1;i<=N;i++) { for(int j=0;j<C[i];j++) F[i][j]=F[i-1][j]; for(int j=C[i];j<=V;j++) F[i][j]=max(F[i-1][j],F[i-1][j-C[i]]+W[i]); }
很显然时间复杂度已将无法优化了,考虑优化空间复杂度。
第二维显然无法舍去,考虑舍去第一维。
定义状态F[j]表示原来的F[i][j],舍去的i将在循环中体现。
F[i][j]是由F[i-1][j]与F[i-1][j-Ci]推导出的,所以我们需要保证F[j]也是由它们推导而出。
事实上,如果我们逆着做j循环,即j从V到0循环,计算F[j]时,F[j-Ci]的值就是F[i-1][j-Ci]的值。
所以我们可以将其优化成一维数组。
关于F数组初值的问题:
如果要求恰好装满背包,那么初始化时F[0]=0,其它均为负无穷,因为除了容量为0的背包可以在什么都不装时价格为0,其它容量的背包都不行(即不合法)。
如果没有要求恰好装满背包,那么初始化时F数组全部为0,因为任何容量的背包都可以不装任何物品价格为0。
//F数组初值为0 for(int i=1;i<=N;i++) for(int j=V;j>=C[i];j--) F[j]=max(F[j],F[j-C[i]]+W[i]
有N件物品和一个容量为V的背包,每种物品可以无限使用,放入第i件物品的费用是Ci,价值是Wi。
求将若干个物品装入背包得到的最大价值。
完全背包和01背包非常相似,唯一的区别就是01背包每种物品只能用一次,而完全背包可以无限使用。
所以考虑将完全背包转化成01背包,仔细想想,01背包在j循环时逆推是为了保证F[i][j]是由F[i-1][v-Ci]推导而来,即每种物品只选一次。
而如果我们正推j循环,即从Ci到V,那么每种物品都可以无限选用,这不正是完全背包吗?
//F数组初值均为0 for(int i=1;i<=N;i++) for(int j=C[i];j<=V;j++) F[j]=max(F[j],F[v-C[i]]+W[i])
有N件物品和一个容量为V的背包,每种物品有Mi个,放入第i件物品的费用是Ci,价值是Wi。
求将若干个物品装入背包得到的最大价值。
依旧转化成01背包求解,把第i种物品转换成Mi件只有1个的物品。
然后用求解01背包的方法来做即可。
int k=n; for(int i=1;i<=n;i++) while(M[i]>1) { C[++k]=C[i]; W[k]=W[i]; M[i]--; } //F数组初值均为0 for(int i=1;i<=k;i++) for(int j=V;j>=C[i];j--) F[j]=max(F[j],F[j-C[i]]+W[i])
【题目描述】
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。
更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。
于是,他把每件物品规定了一个重要度,分为5等:用整数1−5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。
他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[?j],重要度为w[?j],共选中了k件物品,编号依次为j1?,j2?,…,jk?,则所求的总和为:
v[?j1?]×w[?j1?]+v[?j2?]×w[?j2?]+…+v[?jk?]×w[?jk?]。
请你帮助金明设计一个满足要求的购物单。
【输入格式】
第一行,为2个正整数,用一个空格隔开:N m(其中N表示总钱数,m为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j−1的物品的基本数据,每行有2个非负整数v p(其中v表示该物品的价格v,p表示该物品的重要度(1−5)
【输出格式】
1个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
【输入样例】
1000 5 800 2 400 5 300 5 400 3 200 2
【输出样例】
3900
【数据规模】
N<30000,m<25,v≤10000
裸的01背包,只需要注意价值是价格乘上重要度,再套上模板就可以了。
#include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> using namespace std; int n,m,v[99999],p[99999],f[99999],maxn; int main() { memset(f,0xcf,sizeof(f)); f[0]=0; cin>>n>>m; for(int i=1;i<=m;i++) { cin>>v[i]>>p[i]; p[i]*=v[i]; } for(int i=1;i<=m;i++) for(int j=n;j>=v[i];j--) { f[j]=max(f[j],f[j-v[i]]+p[i]); maxn=max(maxn,f[j]); } cout<<maxn; return 0; }
详见本蒟蒻的博客
注:参考书籍:《背包九讲》
原文:https://www.cnblogs.com/I-Love-You-520/p/11428221.html