·0/1背包
//二维数组版
int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j)
vector<int>w(n+10),v=w;//重量数组,价值数组
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=W;j++){
f[i][j]=f[i-1][j];//第i件物品不放
if(j>=w[i]) //第i件物品放得下
f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);//取最大值
}
}
int ans=f[n][W];
//滚动数组版
int W,n; cin>>W>>n;
vector<int>f(W+10);//不要求恰好装满
vector<int>f(W+10,-1e8); f[0]=0;//要求恰好装满
vector<int>w(n+10),v=w;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++)
for(int j=W;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
int ans=f[W];
//0/1背包求方案数
int W,n; cin>>W>>n;
vector<int>f(W+10,1);//不要求恰好装满
vector<int>f(W+10); f[0]=1;//要求恰好装满
vector<int>w(n+10);
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++)
for(int j=W;j>=w[i];j--)
f[j]+=f[j-w[i]];
int ans=f[W];
·完全背包
//暴力:
int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j)
vector<int>w(n+10),v=w;//重量数组,价值数组
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++){
f[i][j]=f[i-1][j];//第i个物品不放的最大值
for(int j=0;j<=W;j++)
for(int k=1;k*w[i]<=j;k++)
f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
}
int ans=f[n][W];
考虑优化:
f[i][j]=max{f[i-1][j],f[i-1][j-w[i]]+v[i],f[i-1][j-2*w[i]]+2*v[i],f[i-1][j-3*w[i]]+3*v[i],...};
f[i][j-w[i]]=max{f[i-1][j-w[i]],f[i-1][j-2*w[i]]+v[i],f[i-1][j-3*w[i]]+2*v[i],f[i-1][j-4*w[i]]+3*v[i],...};
==>
f[i][j]=max(f[i-1][j],f[i][j-w[i]]+v[i]);
//优化 二维数组
int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j)
vector<int>w(n+10),v=w;//重量数组,价值数组
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=W;j++){
f[i][j]=f[i-1][j];
if(j>=w[i])
f[i][j]=max(f[i][j],f[i][j-w[i]]+v[i]);
}
}
int ans=f[n][W];
//滚动数组
int W,n; cin>>W>>n;
vector<int>f(W+10);//不要求恰好装满
vector<int>f(W+10,-1e8); f[0]=0;//要求恰好装满
vector<int>w(n+10),v=w;
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<=W;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
//完全背包求方案数
int W,n; cin>>W>>n;
vector<int>f(W+10,1);//不要求恰好装满
vector<int>f(W+10); f[0]=1;//要求恰好装满
vector<int>w(n+10);
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++)
for(int j=w[i];j<=W;j++)
f[j]+=f[j-w[i]];
int ans=f[W];
·多重背包
//暴力
int W,n; cin>>W>>n;
vector<vector<int>>f(n+10,vector<int>(W+10));
vector<int>w(n+10),v=w,c=w;//重量、价值、个数
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=W;j++){
f[i][j]=f[i-1][j];
for(int k=1;k<=c[i] and k*w[i]<=j; k++)
f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
}
}
int ans=f[n][W];
//空间优化-滚动数组
int W,n; cin>>W>>n;
vector<int>f(W+10);
vector<int>w(n+10),v=w,c=w;
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];
for(int i=1;i<=n;i++){
for(int j=W;j>=w[i];j--){
for(int k=0;k<=c[i] and k*w[i]<=j;k++)
f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
}
}
int ans=f[W];
//时间优化-二进制优化
int W,n; cin>>W>>n;
vector<int>f(W+10);
vector<int>w(n+10),v=w,c=w;
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];
for(int i=1;i<=n;i++){
int num=min(c[i],W/w[i]);//最多选多少个,上界优化
for(int k=1;num>0;k<<=1){
if(k>num) k=num;
num-=k;
for(int j=V;j>=k*c[i];j--)//构造一个重量为k*w[i],价值为k*v[i]的物品,再走0/1背包
f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
}
}
int ans=f[W];
//二维费用背包
//三维
int W,M,n; cin>>n>>W>>M;
vector<vector<vector<int>>>f(n+10,vector<vector<int>>(W+10,vector<int>(M+10)));
vector<int>w(n+10),m=w,v=w;
for(int i=1;i<=n;i++) cin>>w[i]>>m[i]>>v[i];
for(int i=1;i<=n;i++) {
for(int j=0;j<=W;j++)
for(int k=0;k<=M;k++){
f[i][j][k]=f[i-1][j][k];
if(j>=w[i] and k>=m[i])
f[i][j][k]=max(f[i][j][k],f[i-1][j-w[i]][k-m[i]]+v[i]);
}
}
int ans=f[n][W][M];
//空间优化
int W,M,n; cin>>n>>W>>M;
vector<vector<int>>f(W+10,vector<int>(M+10));
vector<int>w(n+10),m=w,v=w;
for(int i=1;i<=n;i++) cin>>w[i]>>m[i]>>v[i];
for(int i=1;i<=n;i++)
for(int j=W;j>=w[i];j--)
for(int k=M;k>=m[i];k--)
f[j][k]=max(f[j][k],f[j-w[i]][k-m[i]]+v[i]);
int ans=f[W][M];
·混合背包
int W,n; cin>>W>>n;
vector<int>f(W+10);
vector<int>w(n+10),v=w,p=w;//p是类型数组
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>p[i];
for(int i=1;i<=n;i++) {
if(p[i]==-1){//0/1背包
for(int j=W;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}else if(p[i]==0){//完全背包
for(int j=w[i];j<=W;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}else{//多重背包
int num=min(p[i],W/w[i]);
for(int k=1;num>0;k++){
if(k>num) k=num;
num-=k;
for(int j=W;j>=k*w[i];j--)
f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
}
}
}
int ans=f[W];
·分组背包
int W,n; cin>>n>>W;//n是组数
vector<int>f(W+10);
for(int i=1;i<=n;i++){
int s;cin>>s; //第i组的物品数
vector<int>w(s+10),v=w;
for(int j=1;j<=s;j++) cin>>w[j]>>v[j];
for(int j=W;j>=0;j--)//容量的循环必须在物品枚举循环的前面,顺序不能颠倒
for(int k=1;k<=s;k++)
if(j>=w[k])
f[j]=max(f[j],f[j-w[k]]+v[k]);//每个容量下的解可以反复覆盖以求得最优解
}
int ans=f[W];
24/7/29
例题:
·设状态:f[j][k]表示选j个人的分数,是否可以凑出k分(f[j][k]表示可以凑出)。
·状态转移方程:f[j][k]|=f[j-1][k-x];(考虑j-1个人的分数之外的另一个人的分数为x情况下,可以凑出k分,这j-1个人的分数值k-x).
·枚举x:
for(int i=1;i<=n;i++)//枚举a[i](上文所述x)
枚举j(所选人数):
for(int j=i;j>=1;j--)//每考虑进来一个新的数,就自顶向下进行一遍状态转移,因为每个人的分数只能选一次,类似0/1背包(倒着转)
枚举分数:
for(int k=a[i];k<=s/2;k++)
状态转移:
f[j][k]|=f[j-1][k-a[i]];
最后倒着遍历分数找答案即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
int f[N][N*N];//f[i][k]只选i个人的请况能否凑出k分
int a[N];
int main()
{
int n,s=0;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s+=a[i];
}
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=i;j>=1;j--)//每考虑进来一个新的数,就自顶向下进行一遍状态转移,因为每个人的分数只能选一次,类似0/1背包(倒着转)
for(int k=a[i];k<=s/2;k++)//这里正反序都无所谓
f[j][k]|=f[j-1][k-a[i]];//状态转移方程
for(int k=s/2;k>=0;k--)
if(f[n/2][k]){
cout<<k;
return 0;
}
return 0;
}
24/8/5
25/5/1