01二维:二维下的状态定义f[i][j]是前 i件物品,背包容量 j下的最大价值
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
int v[N],w[N];
int f[N][N];
int n,m,k;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(j<w[i])//装不下
f[i][j]=f[i-1][j];//上一轮的选择
else
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
}
}
cout<<f[n][m]<<endl;
return 0;
}
二维换一维:f[j]就是前i轮已经决策的物品且背包容量 j 下的最大价值
逆序:为什么要逆序呢?
在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有可能是现在这一轮的f[较小体积]更新到现在这一轮的f[较大体积],但是我们需要的是从i-1轮得到的状态(因为要看没有加该物体时的情况下加不加谁更优)而我们现在这一轮的f[较小体积]有可能已经考虑过这个物体了,会重复考虑。比如说第i层考虑体积3的物体,f[7]由没有考虑该物体的i-1层的max(f[4],f[4]+v[该物体])更新得来。
(1)从小到大0-m:f[4]先计算,私下里更新成了f[i][4],当我们再次遍历f[7]时用的是第i层的f[4],这时候里面的现在的i物体可能已经被决策过了,加入背包里了,可能会重复加入。
(2)从大到小m-0:f[7]先计算,f[7]由f[4]更新而来,f[4]在i层没有被计算过,没有加入到现在的决策背包中。里面所有的价值决策都是从上一层i-1层得来的。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
if(j<w[i])//放不下的话就更新成上一层更新过的f[j]
f[j]=f[j];//不变,可以省略
else
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
我们发现只有当枚举的背包容量 >= v[i] 时才会更新状态,因此我们可以修改循环终止条件进一步优化。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
cout<<f[m]<<endl;
return 0;
}
完全背包:换一下二层循环顺序:因为可以从当前层考虑,反正可以无限次选用
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)
{
for(int j=w[i];j<=m;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
cout<<f[m]<<endl;
return 0;
}
多重背包:
拆成01背包:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 100010
using namespace std;
int w[N],v[N],s[N];
int dp[N];
int n,m;
int main()
{
int a,c,b,t=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a>>b>>c;
while(c--)
{
v[++t]=a;
w[t]=b;
}
}
for(int i=1;i<=t;i++)
{
for(int j=m;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
cout<<dp[m]<<endl;
return 0;
}
朴素版一维:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 110
using namespace std;
int w[N],v[N],s[N];
int dp[N];
int n,m;
int main()
{
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>=1;j--)
{
for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[m]<<endl;
return 0;
}
分组背包:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 110
using namespace std;
/*struct node{
int a,b;//a组号,b个人编号
}v[N],w[N];*/
int v[N][N],w[N][N];
int n,m,t;//n组数、m容量
int s[N];//该组个数
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=1;j<=s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];//不选的话
for(int k=1;k<=s[i];k++)//从编号1开始看看选不选
{
if(j>=v[i][k])//剩余空间可以选择该物体的话
f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}//从不选该组物体中更新就行。
}
}
cout<<f[n][m]<<endl;
return 0;
}
分组背包一维优化:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 110
using namespace std;
/*struct node{
int a,b;//a组号,b个人编号
}v[N],w[N];*/
int v[N][N],w[N][N];
int n,m,t;//n组数、m容量
int s[N];//该组个数
int f[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=1;j<=s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=1;k<=s[i];k++)
{
if(j>=v[i][k])
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[m]<<endl;
return 0;
}