首先说明一下01背包
有一个容量为V的背包,另外有N个物品,每个物品Ni都有其对应的体积Vi和价值Pi
求如何取舍装进背包中使得背包里的总价值P达到最大值
题目看似很像贪心问题,把我们不妨先以贪心的思路解决一下这个问题
每个物品都有其对应的价值与体积,我们将其做比,优先选择其比值较高的物品
设:总容量为100,物品N为3
物品A:容量50 价值9K
物品B:容量40 价值7K
物品C:容量55 价值11K
则贪心的选择顺序为->C->B此时A无法装入,舍弃。 虽然只用了95的总容量,但是其价值确实是最大化,看起来似乎没什么不对。
那么我们再看一组数据
物品A:容量50 价值9K
物品B:容量40 价值11K
物品C:容量65 价值19K
假如我们仍以贪心的思路去选择,我们就会发现,即使C的“性价比”很高,但是我们如果选择了他,就无法再装入其他物品,浪费了容量空间,
还不如选择A+B的价值更大。所以,贪心思想解决不了背包的问题。
问题出在哪里了呢? 对于每一个物品我们都有装和不装两种选择,对于计算机而言,就像是0和1两种状态一样,所以这类问题我们称之为01背包
我们纠结的地方就在于我们单独去看一个物品(亦或几个物品)的时候我们并不知道他到底选还是不选,我们也无法保证当看过n个物品后,我将以
多少剩余容量去面对接下来未知的物品。
所以,我们必须要把看过n个物品之后,所有容量的情况全部考虑进去。
即:外层循环从1到N,用于遍历N个物品,内层循环从0(我们暂且认为没有容量小于0的物品)到V
即浏览到Ni时,任意一个容量值j的最优价值都计算出来
计算的公式为:当前容量值j如果大于Vi,那么有两种选择——选该物品i,那么当前容量j的价值就变成了j-Vi的最优价值+i的价值,或者是不选择该物品,保留j的价值
即 f(i,j)=max{f(i-1,Pj),f(i-1,P[j-v[i]]+Pi
可能有一些绕,举个例子说明:还是上面的数据,假设我们考虑到C:
那么 对于我每一个容量点(0~100)都要进行是否去物品C的判断,比如说容量50,很明显,装不下,比如说容量90,我要看在我不选物品C之前,我容量80时最大的
价值是多少,如果我选择这个物品C,那么我的价值就是 容量为:90-65=25时我能获取的价值 再加上物品C本身的价值。
不加C时容量90的价值为A+B=20k ,加C为容量25(没有合适的物品,价值为0)+C的价值=0+19=19k,很显然,选择C是一种不合适的行为。
也就是我们的代码为:
for(i=1;i<=n;i++) //外层循环物品个数
{
for(j=0;j<=v;j++) //内层循环每个容量点
{
if(j-b[i]>=0)
dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]); //容量大于该物品体积时状态转移方程
else
dp[i][j]=dp[i-1][j]; //容量小于该物品体积,只能舍弃
}
}
想必到此大家应该明白01背包的解决思路了,下面为hdu一道01背包题
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=2602
题目描述:和01背包基本意思一样, 收集骨头
#include<stdio.h> int dp[1020][1020];int fmax(int a,int b){ return a>b?a:b;}int main(){ int b[1020]; int a[1020]; int t,n,i,j,v; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&v);for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<=n;i++) scanf("%d",&b[i]); for(i=1;i<=n;i++){for(j=0;j<=v;j++){if(j-b[i]>=0)dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]);else dp[i][j]=dp[i-1][j];}}printf("%d\n",dp[n][v]);}return 0;}
共同学习,写下你的评论
评论加载中...
作者其他优质文章