背包问题(综合总结)
背包问题,从我们的学习顺序,以及复杂程度上,主要有四个方向:
1、0-1背包
2、完全背包
3、多重背包
4、分组背包
上周总结了个动态规划的小结,在这里再提一下。
动态规划是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。动态规划则通过类似于填表的方式,把所有子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取出子问题的计数结果,避免了重复计算,从而节约了时间。但是,一般动态规划的问题所用的时间都是以幂次方的形式成倍增加的,很容易超时,这就需要我们将代码做到最优,尽量降低时间复杂度,才能保证题目的完成。
0-1背包
上周我主要总结的就是0-1背包,这是背包问题最基础的雏形了。
w[i]表示物品重量,用v[i]表示物品价值,状态dp[i][j]背包容量d为j时放入前i个物品所能获得的的最大价值。
边界条件:dp[0][j]=dp[i][0]=0;
思路:
对于第i件物品,要装入背包,
(1)当背包的剩余容量小于物品i所需要的容量时,那么物品i不能装入背包,此时,前i件物品装入背包的最大价值就等于前i-1件物品装入背包的最大价值。
(2) 当背包的剩余容量大于物品i所需要的容量时,那么物品i能装入背包,此时有两种情况,物品i可以选择装入或者不装入。如果不装入物品i,那么前i件物品装入背包的最大价值就与前i-1件物品装入背包的最大价值相同,即dp[i][j]=dp[i-1][j]。如果装入物品i,那么此时背包装入物品的最大价值就是 dp[i][j]=dp[i-1][j-w]+v[i]。
此时,比较两个价值选择大的,即:
dp[i][j]=max{ dp[i-1][j] ,dp[i-1][j-w]+v[i]}
下面是一道0-1背包的变形问题:
题目:
有n头牛,题目给出了每头牛的高度和书架的高度,然而一头牛可以站在另一头牛的身上,此时的高度是两头牛的高度之和。如果要达到书架的顶端(牛的总高度不低于书架的高度),问满足条件的总高度的最小值与书架高度的差值。
解题思路:此时,书架的高度应该看成背包容量,牛的高度看成体积和价值;dp[i]表示书架高度为i时,牛的高度不超过i的最大高度。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int s[20001];
int main()
{
int n,b,sum,i;
while(scanf("%d%d",&n,&b)==2)
{
for(i=0; i<n; i++)
scanf("%d",&s[i]);
sort(s,s+n);
sum=0;
for(i=n-1; i>=0; i--)
{
if(sum>=b)
break;
else
sum+=s[i];
}
printf("%d\n",n-i-1);
}
return 0;
}
完全背包
完全背包与0-1背包最主要的不同就是遍历方向不同,0-1背包的第二重循环是从后往前遍历,而完全背包是从前往后顺序遍历的。当然了,这主要取决于题目给定条件的不同。0-1背包每件物品只能取一件,而完全背包,可以无限次取,最终只要找到最优解即可。
代码实现的思路:
f[i][j]只与f[i-1][j]和f[i][j-w[i]]+v[i]有关,考虑f[i][j]时,由于是从前往后写,一维数组表示的f[j]还没被写入,它表示的是f[i-1][j],而f[j-w[i]]已经被写入,它表示的是f[i][j-w[i]];
一维的代码实现:f[v]=max{f[v],f[v-w[i]]+v[i]}
二维的代码实现:f[i][