背包问题
1.01背包
问题描述2. 01背包问题 - AcWing题库
你有一个体积固定的背包,要去装宝石,每个宝石都有体积v和价值w,你需要王背包里装进价值最多的宝石(宝石是不可被分割的)
状态表示
f(i,j)在前i中选背包容量为j的最大价值
当前的状态依赖于之前的状态,可以理解为从初始状态f(0,0) = 0开始决策,有 N 件物品,则需要 N次决 策,每一次对第 i件物品的决策,状态f(i,j)不断由之前的状态更新而来。
状态转移方程
(1) 当背包容量不够时(j<v[i]),那我们就没得选,因此前i个物品的最优解其实就是前i-1个物品的最优解
-
f[i][j]=f[i-1][j]
(2) 当背包容量够的时候(j>=v[i]),那我们就可以选,因此需要决策选与不选第i个物品。
-
选:
-
f[i][j]=f[i][j-v[i]]+w[i]
-
不选:
-
f[i][j]=f[i-1][j]
因为我们要是价值最大所以两者取最大值
朴素版代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int v[MAXN]; // 体积
int w[MAXN]; // 价值
int f[MAXN][MAXN]; // f[i][j], j体积下前i个物品的最大价值
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
// 当前背包容量装不进第i个物品,则价值等于前i-1个物品
if(j < v[i])
f[i][j] = f[i - 1][j];
// 能装,需进行决策是否选择第i个物品
else
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
优化
dp问题的优化通常都是在代码上进行等价优化,在此问题上可以使用滚动数组将二维降为一维
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N],w[N];
int f[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[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]<<endl;
return 0;
}
2.完全背包3. 完全背包问题 - AcWing题库
问题描述3. 完全背包问题 - AcWing题库
与01背包问题类似,只是现在每个物品可以使用无限次
状态表示
f(i,j)在前i个中选选出的物品体积不大于j的最优选法
状态表示
在划分集合时我们可以划分成第i个物品选择k个 0,1,2,…j/v[i]
状态转移方程
(1) 当背包容量不够时(j<v[i]*k),那我们就没得选,因此前i个物品的最优解其实就是前i-1个物品的最优解
f[i][j]=f[i-1][j]
(2) 当背包容量够时(j>=v[i]*k)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
朴素版代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N],w[N];
int f[N][N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k*v[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
cout<<f[n][m]<<endl;
return 0;
}
优化
因为三重循环时间复杂度太高为此我们可以将第三重循环优化掉
我们可以发现在可以选的时候
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i],f[i][j-2*v[i]]+2*w[i]...........)
f[i][j-v[i]]=max(f[i][j-v[i]],f[i][j-2*v[i]]+w[i]......)
// 所以我们可以发现f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i])
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N],w[N];
int f[N][N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
}
cout<<f[n][m]<<endl;
return 0;
}
3.多重背包14. 多重背包问题 I - AcWing题库
问题描述
与01背包类似,但是每种物品的数量是给定的
状态表示
f(i,j)在前i个中选选出的物品体积不大于j的最优选法
状态转移方程
(1) 当背包容量不够时(j<v[i]*k),那我们就没得选,因此前i个物品的最优解其实就是前i-1个物品的最优解
f[i][j]=f[i-1][j]
(2) 当背包容量够时(j>=v[i]*k)
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
朴素版代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int v[N],w[N],s[N];
int f[N][N];
int main()
{
int n,m;
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=0;j<=m;j++)
for(int k=0;k<=s[i]&&v[i]*k<=j;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;
}
优化
优化采用的是二进制优化,对于每个物品的数量我们可以把它们变成几个组(每个组中的体积和价值就是这个组的数量乘以单个物品的体积和价值),这样我们可以把第三重循环优化为log级别,最后就是转化为01背包问题了
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25000,M=1010;
int v[N],w[N];
int f[N];
int main()
{
int n,m,cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s)
{
cnt++;
v[cnt]=a*k;
w[cnt]=b*k;
s-=k;
k*=2;
}
if(s)
{
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
n=cnt;
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]<<endl;
return 0;
}
4.分组背包问题9. 分组背包问题 - AcWing题库
问题描述
与01背包类似,现在有n个组每个组都有几个物品现在只能从小组中选择一个物品或者不选,问最后最大的价值是多少
状态表示
f(i,j)在前i个中选选出的物品体积不大于j的最优选法
状态转移方程
与完全背包类似,第三重循环从选几个变成了在每个小组中对于每个物品是选还是不选
朴素版代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int v[N][N],w[N][N],s[N];
int f[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=0;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=0;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;
}