每次增加一个物品放入背包,如果可以放入,设该物品重量为w1,价值v1,得到其放入之后背包的总重量W,减去这个物品的重量w1,那么W-w1这个重量的列的最大价值加上v1如果大于该位置(该行该列)上一行的价值的值,就更新此处的value值,表明这个物品放入背包,不然就继承上一行的value值。
第一种,0-1背包 :
dp:复习dp看
#include<bits/stdc++.h>
using namespace std;
int dp[1005];//滚动数组的写法,省下空间省不去时间
int weight[1005];
int value[1005];
int main()
{
int n,m;
cin>>m>>n;
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
cin>>weight[i]>>value[i];
for(int i=1; i<=n; i++)//对每个数判断,可反
{
for(int j=m; j>=weight[i]; j--)//这里这个循环定死,不能反,反了就是完全背包
{
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);//其实不断在判断最优解,一层一层的
}
}
cout<<dp[m]<<endl;
return 0;
}
优化为一维数组:01背包逆着 完全背包顺着
#include <iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<deque>
#include<queue>
#include<vector>
#include<stack>
#include<ctime>
using namespace std;
#define eps 1e-10
#define INF 0x3f3f3f3f
typedef long long LL;
int main()
{
LL dp[1000],n,m,v[1000],c[1000];
memset(dp,0,sizeof(0));
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i];
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
for(int j=0;j<=m;j++)
cout<<dp[j]<<" ";
cout<<endl;
}
}
第二种,完全背包:
完全被包与0-1背包的区别就是完全背包里的物品数量是无限个,完全背包与0-1背包代码实现相差不大
dp数组正着更新是完全背包,倒着更新是0-1背包;
#include <iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<deque>
#include<queue>
#include<vector>
#include<stack>
#include<ctime>
using namespace std;
#define eps 1e-10
#define INF 0x3f3f3f3f
typedef long long LL;
int main()
{
LL dp[1000],n,m,v[1000],c[1000];
memset(dp,0,sizeof(0));
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i];
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=c[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
for(int j=0;j<=m;j++)
cout<<dp[j]<<" ";
cout<<endl;
}
}
第三种,多重背包:
多重背包问题限定了一种物品的个数,解决多重背包问题,只需要把它转化为0-1背包问题即可。比如,有2件价值为5,重量为2的同一物品,我们就可以分为物品a和物品b,a和b的价值都为5,重量都为2,但我们把它们视作不同的物品。
二进制分解思想:
例如有22个重量和价值都为1的物品,如果按照常规方法的话,需要循环22次,每次放入一个重量质量都为1的物品,而用二进制分解的话,可以将22个物品转化为5个物品,5个物品的重量和价值为1,2,4,8,7;这样致需要循环5次就可以了。
把22进行二进制拆分:
成为1,2,4,8,7;由1,2,4,8可以组成1--15之间所有的数,而对于16--22之间的数,可以先减去剩余的7,那就是1--15之间的数可以用1,2,4,8表示了。
多重背包就是背包里物品数量不为一但是有限,相当于介于0-1背包与完全背包之间,一般解决多重背包问题把
0-1背包与完全背包结合起来使用,而且一般0-1背包可以用二进制压缩来优化,降低时间复杂度
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL dp[100000+10]; //存储最后背包最大能存多少
LL a[100000+10];//存的是物品的价值,或者是重量
LL c[100000+10];//存的是数量
LL m;
void ZeroonePack(LL weight,LL value) // 01背包
{
for(LL i=m;i>=weight;i--)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void completePack(LL weight,LL value) //完全背包
{
for(LL i=weight;i<=m;i++)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void MultiplePack(LL w,LL v,LL num) //多重背包
{
if(num*w>=m){//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
completePack(w,v);
return ;
}
LL k=1; //否则就将多重背包转化为01背包
while(k<=num){
ZeroonePack(k*w,k*v);
num=num-k;
k=k*2; //这里采用二进制思想
}
ZeroonePack(num*w,num*v);
}
int main()
{
LL n;
while(scanf("%lld %lld",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
for(LL i=0;i<n;i++){
scanf("%lld",&c[i]);//输入价值 此题没有物品的重量,可以理解为体积和价值相等
}
for(LL i=0;i<n;i++)
scanf("%lld",&a[i]);
memset(dp,0,sizeof(dp));
for(LL i=0;i<n;i++)
MultiplePack(c[i],c[i],a[i]);//调用多重背包,注意穿参的时候分别是重量,价值和数量
LL ans=0;
for(LL i=1;i<=m;i++)
if(dp[i]==i)
ans++;
cout<<ans<<endl;
}
}

被折叠的 条评论
为什么被折叠?



