0/1背包
内容参考书籍《算法竞赛入门到进阶》
0/1背包是最经典的DP问题,没有之一。
背包问题:有多个物品,重量不同、价值不同,以及一个容量有限的背包,选择一些物品装到背包中,问怎么才能使装进背包的物品总价值最大
如果每个物体可以切分,则是一般背包问题,使用贪心法。例如吃自助餐,要使吃到肚子里的东西价值最大,只需要从最贵的食物吃起即可。
如果物体不可分割就称为0/1背包问题,例如食物都是一份一份的,每一份都必须吃完。如果目前最贵的一份超过了你的饭量,那只好放弃,这样就不能用贪心法求解了。
给定n种物品,背包i的重量是wi 、价值为vi ,背包的总容量为C。在装入背包的物品时对每种物品i只有两种选择,装还是不装也就是所谓的0/1。如何选择物品,使得装入背包的总价值最大呢?
给定一个例子:有4个物品,其重量分别是2、3、6、5,价值分别为6、3、5、4,背包的容量为9.引入一个(n+1)x(C+1)的二维表dp[][],可以把每个dp[i][j]都看成一个背包,dp[i][j]表示把前i个物品装入容量为j的背包中获得的最大价值,i是纵坐标,j是横坐标。
一:假设只装第一个物品。
由于物品重量是2,所以背包容量小于2的都装不进去,得dp[1][0]=dp[1][1]=0;物品1的重量等于背包容量,可以装,价值为物品1的价值,dp[1][2]=6;容量大于2的背包,多于容量用不到,所以价值和容量2的背包一样。
二:在一的基础上增加第二个物品。
如果物品二的重量比背包容量大,那么不可能装物品2,从背包容量等于物品二开始:
(1)如果装物品2(重量是3),那么相当于只把物品1装到容量减3的背包中。
(2)如果不装物品2,那么相当于只把物品1装到背包中
(3)取(1)和(2)的最大值,得dp[2][3]=max(3,6)=6。
三:继续增加重复二,最后输出最大值即可。
那如果要求输出0/1背包方案呢?这需要我们倒过来观察:
dp[4][9]=max{dp[3][4]+4,dp[3][9]}=dp[3][9],说明没有装物品4,用x4=0表示;
dp[3][9]=max{dp[2][3]+5,dp[2][9]}=dp[2][3]+5=11,说明装了物品3,用x3=1表示;
dp[2][3]=max{dp[1][0]+3,dp[1][3]}=dp[1][3],说明没有装物品2,用x2=0表示;
dp[1][3]=max{dp[0][1]+6,dp[0][3]}=dp[0][1]+6,说明装了物品1,用x1=1表示;
练习:hdu2602:http://acm.hdu.edu.cn/showproblem.php?pid=2602
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int a[1010]; 4 int b[1010]; 5 int ans[1010]; 6 int main(int argc, char const *argv[]) 7 { 8 int t; 9 cin>>t; 10 while(t--) 11 { 12 memset(a,0,sizeof(a)); 13 memset(b,0,sizeof(b)); 14 memset(ans,0,sizeof(ans)); 15 int n,m; 16 cin>>n>>m; 17 for (int i = 0; i < n; ++i) 18 { 19 cin>>a[i]; 20 } 21 for (int i = 0; i < n; ++i) 22 { 23 cin>>b[i]; 24 } 25 for (int i = 0; i < n; ++i)//遍历每个物品 26 { 27 for (int j = m; j >= b[i]; --j)//从每个物品的体积开始往后遍历 28 { 29 ans[j]=max(a[i]+ans[j-b[i]],ans[j]); 30 //cout<<ans[j]<<" "; 31 } 32 //cout<<endl; 33 } 34 cout<<ans[m]<<endl; 35 } 36 return 0; 37 }