01背包题目的雏形是:php
有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可以使价值总和最大。ios
从这个题目中能够看出,01背包的特色就是:每种物品仅有一件,能够选择放或不放。c++
其状态转移方程是:spa
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}.net
对于这方方程其实并不难理解,方程之中,如今须要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],所以f[i-1][v]表明的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是表明将第i件放入背包以后的总价值,比较二者的价值,得出最大的价值存入如今的背包之中。code
题目描述:blog
假设山洞里共有a,b,c,d ,e这5件宝物(不是5种宝物),它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,如今给你个承重为10的背包, 怎么装背包,能够才能带走最多的财富。内存
有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,如今给你个承重为10的背包,如何让背包里装入的物品具备最大的价值总和?ci
此例题参考博客:动态规划之01背包问题(最易理解的讲解)get
先对表进行大概的说明,前面三列是物品名字,重量和价值。后面1到10表示背包容量。
e3表示背包容量为3,当前物品为e。 装不下
d3表示背包容量为3,当前物品为e,d。装不下
c3表示背包容量为3,当前物品为e,d,c。 装不下
b3表示背包容量为3,当前物品为e,d,c,b 能够装b物品,因此值为b物品的价值3
a3表示背包容量为3,当前物品为e,d,c,b ,a 这时根据状态转移方程,比较b3和b1+a的价值的值。
后者更大,因此a3值为6.
当时看了上面博客的讲解后本身画出了上面这张表,但是比较好奇为何要从最后一件物品开始往里面装,不能从第一件物品直接开始装嘛?
因而再次从第一件物品以一样的填法进行填表,发现其实结果是同样的,并无影响。
这里对e8进行说明,d8为11,当前容量8减去e的重量4=4,因此寻找d4值 为9 9+6=15>11 因此e8的值更新为15
一道例题:有一个容量为m(1<=m<=4000000)的背包,有n(1<=n<=16)个物品,每一个物品有体积v(1<=v<=2012)和价值w(0<=2012),如今要你选择一些物品,使得背包所装物品的总价值最大。
C++代码:
#include <iostream> #include <memory.h> using namespace std; struct wupin { int v;//体积 int w;//价值 }a[10000]; int dp[10000][10000]; int main() { int n,m; cin>>m>>n; memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) cin>>a[i].v>>a[i].w; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(j>=a[i].v) dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i].v]+a[i].w); else dp[i][j]=dp[i-1][j]; } // for(int i=1;i<=n;i++) // { // for(int j=1;j<=m;j++) // cout<<dp[i][j]<<' '; // cout<<endl; // } cout << dp[n][m] << endl; return 0; }
通常写法:
大小为j的背包是否放第i个物品,取决于大小为(j-a[i].v)的背包的最优解+a[i].w 是否大于当前不放这个物品的最优解的价值
#include <iostream> #include <memory.h> using namespace std; struct wupin { int v;//体积 int w;//价值 }a[10000]; int dp[100000]; int main() { int n,m; cin>>m>>n; memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); for(int i=0;i<n;i++) cin>>a[i].v>>a[i].w; for(int i=0;i<n;i++) for(int j=m;j>=a[i].v;j--) dp[j]=max(dp[j],dp[j-a[i].v]+a[i].w); cout << dp[m] << endl; return 0; }
最简洁的写法:
#include <cstdio> #include <algorithm> using namespace std; int f[1005]; int n,m; int main() { int w,v; scanf("%d%d",&m,&n); for(int i=1;i<=n;i++) { scanf("%d%d",&v,&w); for(int j=m;j>=v;j--) f[j]=max(f[j],f[j-v]+w); } printf("%d\n",f[m]); return 0; }
补充:求背包当前容量为m时,共有多少种不一样选择。
例题:
时间限制: 1 Sec 内存限制: 64 MB
提交: 77 解决: 47
[提交][状态][讨论版][命题人:lyh]
有一个神奇的口袋,总的容积是40,用这个口袋能够变出一些物品,这些物品的整体积必须是40。John如今有n个想要获得的物品,每一个物品的体积分别是a1,a2……an。John能够从这些物品中选择一些,若是选出的物体的整体积是40,那么利用这个神奇的口袋,John就能够获得这些物品。如今的问题是,John有多少种不一样的选择物品的方式。
输入的第一行是正整数n (1 <= n <= 20),表示不一样的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。
输出不一样的选择物品的方式的数目。
3 20 20 20
3
#include<bits/stdc++.h> using namespace std; int a[50]; int b[50][50];//b[i][j]表示从i件物品中凑出j体积的方法数 int main() { int n; cin>>n; int cnt=0; for(int i=1; i<=n; i++) { cin>>a[i]; b[i][0]=1; } b[0][0]=1; for(int j=1; j<=40; j++)//遍历背包容量 for(int i=1; i<=n; i++) { b[i][j]=b[i-1][j]; if(j>=a[i]) { b[i][j]+=b[i-1][j-a[i]]; } } cout<<b[n][40]; return 0; }