完全背包问题
完全背包与0/1背包很类似,推导过程和思维也差不多
原题链接:3. 完全背包问题 - AcWing题库
题目描述:
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
朴素算法代码
首先让我们来看看朴素算法的思想
与0/1背包问题类似,我们第i个物品可以选0个,那么dp[ i,j] = dp[i-1,j];
我们也可以选则1个,2个,3个,…,k个
同样直接算并不是很好算,那我们开始曲线救国,先从前i-1种类物品里面选择,然后再加上第i种物品的选择情况,即 dp[ i -1,j-k*v[i] ] +k * w[ i ],我们不难发现,其实选0个可以与后面的合并,也就是k==0的情况
代码如下
#include<iostream>
using namespace std;
const int N=1010;
int n,V;
int dp[N][N];
int v[N],w[N];
int main(){
cin>>n>>V;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){ //控制选前几件
for(int j=0;j<=V;j++){ //控制当前的体积
for(int k=0;k*v[i]<=j;k++){
//max第一是dp[i][j]的原因是有不同的k产生不同的dp[i][j],所以max找到最大的自己
//max第二个用的是dp[i-1]是因为,我们单独把第i个种类拿出来单独考虑
//当k==0时,dp[i][j] =max(dp[i][j],dp[i-1][j]) 所以不需要分开选择
dp[i][j] = max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[n][V];
return 0;
}
方程展开优化法
#include<iostream>
using namespace std;
const int N=1010;
int n,V;
int dp[N][N];
int v[N],w[N];
//朴素算法推导优化过程
//将dp[i][j]和dp[i][j-v]展开我们不难发现,dp[i][j-v]与dp[i][j]仅相差一个w[i]
// dp[i,j] = max(dp[i,j],dp[i-1,j-1v]+1w,dp[i-1,j-2v]+2W,dp[i-1,j-3v]+3w,dp[i-1,j-4v]+4w,....)
// dp[i,j-v]=max(dp[i,i-v], dp[i-1,j-2v]+1w,dp[i-1,j-3v]+2w,dp[i-1,j-4v]+3w,....)
//综上可得出新的方程
// dp[i,j] = max(dp[i,j],dp[i,j-v]+w);
int main(){
cin>>n>>V;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){ //控制选前几件
for(int j=0;j<=V;j++){ //控制当前的体积
//优化完后要考虑选与不选两种情况
//优化前的代码没考虑是因为不选的情况也包含在里面了,就是k==0时的情况
dp[i][j] = dp[i-1][j];
if(j>=v[i]) dp[i][j] = max(dp[i][j],dp[i][j-v[i]]+w[i]);
}
}
cout<<dp[n][V];
return 0;
}
优化为一维
#include<iostream>
using namespace std;
const int N=1010;
int n,V;
int dp[N];
int v[N],w[N];
int main(){
cin>>n>>V;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){ //控制选前几件
for(int j=v[i];j<=V;j++){ //控制当前的体积
//因为这次二维我们使用的是dp[i][j-v[i]],用的是i层而不是i-1层,所以我们不需要倒叙遍历,正序即可
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[V];
return 0;
}