文章目录
01背包
问题描述:容量为V的背包,n件物品,每件物品的价值为v[i],占空间为w[i],尽量让背包装的价值大。
解法:
寻找递推关系式:
面对当前的物品有两种选择:
(1)不选择,那么此时的价值与前面的价值相同,v[i][j]=v[i-1][j]
(2)选上,那么此时的价值就是前面的总物品的价值加上当前物品的价值,容量减去当前物品的容量 v[i][j]=v[i-1][j-w[i]]+v[i]
如果现在总的容量不够,那么只能不选。
所以推出关系式v[i][j]=max(v[i-1][j],v[i-1][j-w[i]]+v[i]).
Bone Collector
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e3+10;
int n,V;
int w[maxn],v[maxn];
int dp[maxn][maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&V);
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
for(int j=0;j<=V;j++){
if(j>=w[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
else dp[i][j]=dp[i-1][j];
}
}
printf("%d\n",dp[n][V]);
}
return 0;
}
滚动优化
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e3+10;
int n,V;
int w[maxn],v[maxn];
int dp[maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&V);
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
for(int j=V;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
printf("%d\n",dp[V]);
}
return 0;
}
P2925 [USACO08DEC]干草出售Hay For Sale
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
int c,t;
int w[maxn];
int dp[maxn];
int main(){
scanf("%d%d",&c,&t);
for(int i=1;i<=t;i++)
scanf("%d",&w[i]);
for(int i=1;i<=t;i++){
for(int j=c;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
}
}
printf("%d\n",dp[c]);
return 0;
}
完全背包
参考博客
与01背包最大的区别是,每种物品可以选无数次。
完全背包其实也可以理解为很多物品的01背包,因为总容量毕竟是有限的,所以只选一种物品不会真正无穷。
所以递推关系式为:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-kw[i]]+kv[i]) k(0~j/w[i])
按照暴力来表示:
三重循环,肯定是不可以的
for(int i=1;i<=n;i++){
for(int j=0;j<=V;j++){
for(int k=0;k<=j/w[i];k++){
}
}
}
我们假设dp[i][j]表示出在前i种物品中选取若干件物品放入容量为j的背包所得的最大价值。
那么对于第i种,如果不选的话那么dp[i][j]=dp[i-1][j],
如果选的话,那么至少选择一件,dp[i][j]=dp[i][j-w[i]],为什么是dp[i][j-w[i]]呢,因为不知道第i件是否已经选择。
优化三重到两重
公式推导
dp[i][j]=max(dp[i-1][j],dp[i-1][j-kw[i]]+kv[i]);
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i],dp[i-1][j-2w[i]]+2v[i],…,)
dp[i][j-w[i]]=max(dp[i-1][j-w[i]],dp[i-1][j-2w[i]],dp[i-1][j-3w[i]]…)
所以dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]])
for(int i=1;i<=n;i++){
for(int j=0;j<=V;j++){
dp[i][j]=dp[i-1][j];
if(j>=w[i])
dp[i][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
再进一步优化
for(int i=1;i<=n;i++){
for(int j=v[i];j<=V;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
01背包与多重背包的区别
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int dp[maxn],v[maxn],w[maxn];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&v[i],&w[i]);
}
for(int i=1;i<=n;i++){
for(int j=w[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
cout<<dp[j]<<" ";
}
cout<<endl;
}
cout<<endl;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=m;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
cout<<dp[j]<<" ";
}
cout<<endl;
}
return 0;
}
P1616 疯狂的采药
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll t;
const int maxn=1e4+10;
const int maxn1=1e5+10;
ll w[maxn],v[maxn];
ll dp[maxn1];//注意是跟t
int main(){
scanf("%lld%d",&t,&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&w[i],&v[i]);
for(int i=1;i<=n;i++){
for(int j=w[i];j<=t;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
printf("%lld\n",dp[t]);//注意
return 0;
}
多重背包
多重背包是规定了每一个物品的数量
f[i][j]=max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]) //(0<=k<=p[i])
方法:我们可以将第i种换成若干件物品转化为01背包。
for(int i=1;i<=n;i++){
for(int k=1;k<=m[i];k++){
for(int j=V;j>=w[i];j--){
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
}
如果是上面这样的话对于大一点的数据显然是不行的。
比如说7 把它分成1 1 1 1 1 1 1 这7种物品
这7种物品又分选和不选 也就是0~7
那么接下来就是二进制拆分 分成1 2 4
如果是0 ,那么1 2 4都不选择
如果是1,那么选择1
如果是2,选择2
如果是3,选择3
如果是5,选择1和4
…也就是说0~7可以用这三个数表示
再比如10,那就是1,2,4,3
其中每件物品都有一个系数p[i],使这些系数为1,2,4,…,2^(k-1),p[i]-2 ^k+1,且k是满足p[i]-k+1>0的最大整数,例如p[i]=13,则分为1,2,4,6。
这样我们将O(N∑pi)转化为O(N∑log(pi))
二进制代码如下:
for(int i=1;i<=n;i++){
int num=min(p[i],V/w[i]);
for(int k=1;num>0;k<<=1){
if(k>num)k=num;
num-=k;
for(int j=V;j>=w[i]*k;j--)
f[j]=max(f[j],f[j-w[i]*k]+k*v[i]);
}
}
P1776 宝物筛选
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e2+10;
const int maxn1=4e4+10;
int n,V;
int v[maxn],w[maxn],m[maxn];
int dp[maxn1];
int main(){
scanf("%d%d",&n,&V);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&v[i],&w[i],&m[i]);
for(int i=1;i<=n;i++){
int num=min(m[i],V/w[i]);
for(int k=1;num>0;k<<=1){
if(num<k) k=num;
num-=k;
for(int j=V;j>=k*w[i];j--){
dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);
}
}
}
printf("%d\n",dp[V]);
return 0;
}
来源:CSDN
作者:Deam-chasing ant
链接:https://blog.csdn.net/qq_43721152/article/details/104460812