01背包问题——每件物品最多只用一次
状态表示:f[i][j] 表示从前i个物品中选,重量不超过j的最大价值。
状态计算:f[i][j] =max(f[i-1][j] , f[i-1][j-v[i]] +w[i] )(当前状态可分为选了第i件物品的价值最大值和没选第i件物品的最大值)
详细描述:
有N件物品和一个容量是V的背包,每件物品只能使用一次。
第i件物品的体积是vi 价值是wi。
求解将哪些物品装进背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
分析:
f[i][j]:
数组含义:[0,i]之间物品任取,放进容量为j的背包
1.不含第i个物品:f[i][j] = f[i - 1][j];
不选(跳过)这个物品,那么容量j就不会改变:即从[0,i-1]之间的物品随便取,因为不放物品i,那么背包容量的最大价值是不是就和i-1的最大价值是一样的
2.选第i个物品:f[i][j] = f[i - 1][j - v[i]]+w[i];
直接求不好求,先预留第i个物品的空间,最后再加上第i个物品价值
物品量\背包容积 | 0 | 1 | 2 | 3 | 4 | 5 |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(价值2) | 0 | 2 | 2 | 2 | 2 | 2 |
2(价值4) | 0 | 2 | 4 | 6 | 6 | 6 |
3(价值4) | 0 | 2 | 4 | 6 | 6 | 8 |
4(价值5) | 0 | 2 | 4 | 6 | 6 | 8 |
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;//定义最大物品的数量
int n,m;//n表示物品数量,m表示背包容积
int v[N],w[N];//vi表示体积,wi表示价值
int f[N][N];//fij表示所有方案中,从前i个物品中选并体积不超过j的最大价值
int main()
{
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];
//注意,包含第i个物品这种情况可能不存在,因此要判断
if(j>=v[i])//比较背包与物品体积
{
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
优化版
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m;
int f[N]; //这里进行降维优化
int v[N],w[N];
int main()
{
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[i-1][j-v[i]]为了更好地表示发生变化,我们应选择体积从最大的体积m依次递减
f[j] = max(f[j],f[j - v[i]] + w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
完全背包——每件物品能拿无数个
状态表示:f[i][j] 表示从前i个物品中选,重量不超过j的最大价值。
状态计算:f[i][j] =max(f[i-1][j] , f[i][j-v[i]] +w[i],f[i][j-2v[i]] +2w[i],……,f[i][j-kv[i]] +kw[i] )(当前状态可分为选了1-k件第i件物品的价值最大值和没选第i件物品的最大值)
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;//定义最大物品的数量
int n,m;//n表示物品数量,m表示背包容积
int v[N],w[N];//vi表示体积,wi表示价值
int f[N][N];//fij表示所有方案中,从前i个物品中选并体积不超过j的最大价值
int main()
{
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-v[i]*k]+w[i]*k);
cout<<f[n][m]<<endl;
return 0;
}
优化版:
for(int i = 1 ; i <=n ;i++)
for(int j = 0 ; 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]);
}
多重背包|——第i件物品最多可选si件
多个同种物品合成一件物品
把每件物品可选的情况拆分出来,把选择多件第i个物品转换成选择一个新的物品(如选k件则新物品表示为价值为kw[i],体积为kv[i])将问题转变为01背包问题。
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int f[N][N],v[N],w[N],s[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*v[i]<=j&&k<=s[i];k++)
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+k*w[i]);
cout<<f[n][m];
return 0;
}
多重背包问题||——同多重背包问题优化
【【背包问题】多重背包问题 II】https://www.bilibili.com/video/BV1Q7411j7Vi?vd_source=cecc6ff6e883f42b10c1d53026d0846e
二进制优化——>转化成01背包,2^n 件
#include<bits/stdc++.h>
using namespace std;
const int N=15000;
int f[N],v[N],w[N];
int main(){
int n,m,tot=1;//变量存放打包的编号
cin>>n>>m;
int a,b,s;//体积,价值,个数
for(int i=1;i<=n;i++){
cin>>a>>b>>s;
//开始打包
for(int k=1;k<=s;k<<=1){//k*2
v[tot]=k*a;
w[tot++]=k*b;
s=s-k;
}
if(s>0)//打包完还有剩余的 比如10=1+2+4+3的3
{
v[tot]=a*s;
w[tot++]=b*s;
}
}
//回到01背包问题
for(int i=1;i<=tot;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m];
return 0;
}
分组背包问题——分为n组,每组有ni个物品,每组最多只能选一件物品。