背包问题专栏(01,完全,多重)
Posted crazily
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包问题专栏(01,完全,多重)相关的知识,希望对你有一定的参考价值。
A.S.KirigiriKyouko
的模板。请dalao见谅
一、01背包
有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这是最基础的背包问题,总的来说就是:选还是不选,这是个问题<( ̄ˇ ̄)/
相当于用f[i][j]表示前i个背包装入容量为v的背包中所可以获得的最大价值。
对于一个物品,只有两种情况
情况一: 第i件不放进去,这时所得价值为:f[i-1][v]
情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]
状态转移方程为:f[i][v] = max(f[i-1][v], f[i-1][v-w[i]]+c[i])
一道裸01背包题↓_↓
采药
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入第一行有两个整数T(1<=T<=1000)和M(1<=M<=100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
70 3
71 100
69 1
1 2
3
【数据规模】
对于30%的数据,M<=10;
对于全部的数据,M<=100。
//01背包问题 (采药) //by Crazily #include<bits/stdc++.h> using namespace std; int w[105],v[105]; int dp[105][1005]; int main(){ int t,m; scanf("%d%d",&t,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&w[i],&v[i]); } for(int i=1;i<=m;i++) for(int j=t;j>=0;j--){ if(j>=w[i]){ dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]); } else{ dp[i][j]=dp[i-1][j]; } } printf("%d",dp[m][t]); }
再上一维的
#include<bits/stdc++.h> using namespace std; const int maxm = 2001, maxn = 101; int m, n; int w[maxn], c[maxn]; int f[maxm]; int main(){ scanf("%d%d",&m, &n); //背包容量m和物品数量n for (int i=1; i <= n; i++) scanf("%d%d",&w[i],&c[i]); //每个物品的重量和价值 for (int i=1; i <= n; i++) //设f(v)表示重量不超过v公斤的最大价值 for (int v = m; v >= w[i]; v--) //注意是逆序 f[v] = max(f[v-w[i]]+c[i], f[v]); printf("%d ",f[m]); // f(m)为最优解 return 0; }
二、完全背包
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
完全背包和01背包十分相像, 区别就是完全背包物品有无限件。由之前的选或者不选转变成了选或者不选,选几件。√
和01背包一样,我们可以写出状态转移方程:f[i][v]=max(f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v)
还有一个简单的优化↓_↓
当一个物品的价值小于另一个物品的价值,但是价格高于另一个物品,我们就可以不去考虑这个物品。即若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。我们为什么要买一个又贵又难吃的东西呢(╯▽╰)
#include <iostream> #define V 500 using namespace std; int weight[20 + 1]; int value[20 + 1]; int f[V + 1]; int max(int a, int b) { return a > b ? a : b; } int main() { int n, m; cin >> n; for (int i = 1; i <= n; i++) { cin >> weight[i] >> value[i]; } cin >> m; for (int i = 1; i <= n; i++) { for (int j = weight[i]; j <= m; j++) { f[j] = max(f[j], f[j - weight[i]] + value[i]); } } cout<< f[m] << endl; }
三、多重背包
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这里又多了一个限制条件,每个物品规定了可用的次数。
同理,我们可以得出状态转移方程:f[i][v]=max(f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i])
一道例题↓_↓
庆功会
【问题描述】
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
【输入格式】
第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。 接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可),其中v<=100,w<=1000,s<=10。
【输出格式】
第一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
【输入样例】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【输出样例】
1040
#include<stdio.h> #include<algorithm> using namespace std; int v[6002], w[6002], s[6002]; int f[6002]; int n, m; int main() { scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) scanf("%d%d%d",&v[i],&w[i],&s[i]); for (int i = 1; i <= n; i++) for (int j = m; j >= 0; j--) for (int k = 0; k <= s[i]; k++) { if (j-k*v[i]<0) break; f[j] = max(f[j],f[j-k*v[i]]+k*w[i]); } printf("%d ",f[m]); return 0; } //这是未优化的
再用二进制优化转换成01
#include<stdio.h> #include<algorithm> using namespace std; int v[10001],w[10001]; int f[6001]; int n,m,n1; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { int x,y,s,t=1; scanf("%d%d%d",&x,&y,&s); while (s>=t) { v[++n1]=x*t; w[n1]=y*t; s-=t; t*=2; } v[++n1]=x*s; w[n1]=y*s; //把s以2的指数分堆:1,2,4,…,2^(k-1),s-2^k+1, } for(int i=1;i<=n1;i++) for(int j=m;j>=v[i];j--) f[j]=max(f[j],f[j-v[i]]+w[i]); printf("%d ",f[m]); return 0; }
//先借用一下dalao的代码,有空我会慢慢码上的
以上是关于背包问题专栏(01,完全,多重)的主要内容,如果未能解决你的问题,请参考以下文章
动态规划背包问题总结:01完全多重与其二进制优化分组背包 题解与模板