【分析】
(一)二维
1.f[i][j]:前i个物品容量为j的最优解。
当前的状态依赖于之前的状态,可以理解为从初始状态f[0][0] = 0开始决策,有 N 件物品,则需要 N 次决 策,每一次对第 i 件物品的决策,状态f[i][j]不断由之前的状态更新而来。
2.如果当前背包容量不够( j<v[i]),则当前的最优解由上一次的状态转移而来(f[i][j]=f[i-1][j])
3.对于容量足够的情况,我们有选择的余地,因而要对当前第i件物品进行选与不选的决策。
(1)选:f[i][j]=f[i-1][j-v[i]]+w[i](前 i−1i−1 件物品放入剩下的容量为 j−v[i]j−v[i] 的背包中的最大价值)
(2)不选:f[i][j]=f[i-1][j]
我们需要的是最大价值,因此每次对以上两种情况取max。
【代码1】
#include <bits/stdc++.h>
using namespace std;
int f[35][205];
int v[50],w[50];//v为空间 w为价值
int main(){
int m,n;
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++){//物品数量的枚举
for(int j=0;j<=m;j++){//背包空间的枚举
f[i][j]=f[i-1][j];//承接上一个的最大价值
if(v[i]<=j) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);//当能够放得下时 比较原来的价值和放入当前物品后的价值 取较大值
}
}
cout<<f[n][m];
return 0;
}
(二)一维
1.为什么可以优化成一维
首先观察状态转移方程, f[i][j]是由 f[i-1][jxxxx]推导而来,仅看第一个维度,即i - 1 与 i ,可以发现第i层是由上一层推导而来的。
故我们不需要保存i - 2 层,比如我们计算第三层是只需要第二层的,不需要第一层的数据。
当我们去掉i时,即我们不需要控制第几层,只需要长度为j的数组,保存确认过最新的一层。作为下一层的参考。
2.为什么第二层循环需要逆序
一维的f数组保存的是确认过的最新一层的数据,即上一层的数据。也就是说,我们要得到当前第i层的最优解,必须要从i-1层得到。如果正序,那么我们需要用到的i-1层是已经更新过的,不是原本上一层的i-1了。
相反,如果倒叙,从大到小就不存在这种问题,前面的第i-1层还是原来的i-1层。
3.状态转移方程:f[j]=max(f[j],f[j-v[i]+w[i]]
【代码2】
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int n[N],w[N];//v是重量 w是价值
int f[N];//f[i][j]表示前i件物品在j个空间内能获得的最大价值
int main(){
int m,n;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){//物品枚举
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m];
}
#include <bits/stdc++.h>
using namespace std;
int t[105],v[105];
int a[1005];
int main(){
int T,M;
cin>>T>>M;
for(int i=1;i<=M;i++){
cin>>t[i]>>v[i];
}
for(int i=1;i<=M;i++){
for(int j=T;j>=0&&j>=t[i];j--){
a[j]=max(a[j],a[j-t[i]]+v[i]);
}
}
cout<<a[T];
return 0;
}
【分析】
【代码】
#include <bits/stdc++.h>
using namespace std;
int f[1005];
int v[1005],w[1005];
int main(){
int m,n;
cin>>m>>n;
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<=m;j++){//空间枚举 在当前物品空间小于背包空间的情况下
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<"max="<<f[m];
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int p[5]={0,10,20,50,100};//定义p数组存放每种书的价格
int m,f[1005];//m为书的种类
int main(){
m=4;//一共有四种书可供购买
int n;
cin>>n;
f[0]=1;//初始化
for(int i=1;i<=m;i++){
for(int j=p[i];j<=n;j++){//完全背包
f[j]+=f[j-p[i]];
}
}
if(n==0) cout<<0;//n等于0时 特判
else cout<<f[n];
return 0;
}
【分析】
考虑k的个数 并且需要将多重背包转换成01背包
【代码1】
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int v[N],w[N],s[N];//v是重量 w是价值 s是物品数量
int f[N];//f[i][j]表示前i件物品在j个空间内能获得的最大价值
int main(){
int m,n;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){//物品枚举
for(int j=m;j>=v[i];j--){//空间从大到小
for(int k=1;k<=j/v[i] &&k<=s[i];k++)//k为可以使用的次数且不超过最大数量
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);//多乘个k就🆗
}
}
cout<<f[m];
}
【代码2】二进制优化版
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;//2e5=2*10^5 大一点
int f[N];
int v[N],w[N],s[N];//v体积 w价值 s数量
int main(){
int n,m;
cin>>n>>m;
int l=0;//l用来表示堆数
int a,b,c;//a表示单个物品的体积 b表示单个物品的价值 c表示该物品的个数
for(int i=1;i<=n;i++){
cin>>a>>b>>c;
int k=1;//初始化放入的个数k 为一个
while(c>=k){//当剩余的数量比k大时
l++;//开下一个堆
v[l]=a*k;
w[l]=b*k;//l个物品的空间和价值分别为单个空间和价值乘以k个
c-=k;//每放入k个需要在c中减去
k*=2;//k二进制递加
}
if(c>0){//如果还有剩余的物品
v[++l]=a*c;
w[l]=b*c;//将剩余的空间和价值全部加进数组中
}
}
for(int i=1;i<=l;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);//01背包处理
}
}
cout<<f[m];
return 0;
}
【代码】
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int v[N],w[N];
int f[N];
int main(){
int n,m;
cin>>n>>m;
int a,b,c,l=0;
for(int i=1;i<=n;i++){
cin>>a>>b>>c;
if(c==0){
for(int j=a;j<=m;j++){
f[j]=max(f[j],f[j-a]+b);
}//完全背包
}
else{
if(c==-1) c=1;
int k=1;
while(c>=k){
v[++l]=a*k;
w[l]=b*k;
c-=k;
k*=2;
}
if(c){
v[++l]=c*a;
w[l]=c*b;
}
}
}//将多重背包转换成01背包 详见例3
for(int i=1;i<=l;i++){//从1到l
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m];
return 0;
}
【代码】
#include <bits/stdc++.h>
using namespace std;
int a[1005],b[1005],c[1005];
int f[1005][1005];
int main(){
int s,n,m;
cin>>s>>n>>m;
for(int i=1;i<=s;i++){
cin>>a[i]>>b[i]>>c[i];
}
for(int i=1;i<=s;i++){
for(int j=n;j>=a[i];j--){
for(int k=m;k>=b[i];k--){
f[j][k]=max(f[j][k],f[j-a[i]][k-b[i]]+c[i]);
}
}
}
cout<<f[n][m];
return 0;
}
【代码】
#include <bits/stdc++.h>
using namespace std;
int v[1005][1005],w[1005][1005];
int f[10005][10005];
int a,b,c;
int V,N,T;
int main(){
cin>>V>>N>>T;
for(int i=1;i<=N;i++){
cin>>a>>b>>c;
v[c][0]++;//第c组有多少个物品
int l=v[c][0];
v[c][l]=a;
w[c][l]=b;//将每个物品的重量和价值存入数组中
}
for(int i=1;i<=T;i++){//组别枚举
for(int j=0;j<=V;j++){//容量枚举
f[i][j]=f[i-1][j];//不选的情况下 承接上一个
for(int k=0;k<=v[i][0];k++){//当前组别中每种物品的枚举
if(j>=v[i][k]) f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);//在容量j大于当前物品的重量的情况下(否则会出现复数)
}
}
}
cout<<f[T][V];
}