01背包(每个物品只能取一次)
01背包和完全背包优化区别在于01是倒序,完全是正序
/*我们每次计算dp[j] (即dp[i][j]) 的时候都会需要dp[j-w[i]]
(即dp[i-1][j-w[i]])的值。
因为j-w[i]比j小,所以如果我们正序计算,(正序用完全,因为完全可以重复,不需要考虑更新)
那么dp[j-w[i]]就已经更新了 (即dp[i][j-w[i]]),与状态转移方程不符。
#includeusing namespace std; int n,m,v[1001],w[1001],f[1001],g[1001]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&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]); } } printf("%dn",f[m]); }
完全背包(每个物品可取多次)
#includeusing namespace std; int n,m,v[1001],w[1001],f[1001],g[1001]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&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]); } } printf("%dn",f[m]); system("pause"); }
多重背包(规定一个物品可以取多次,转换成01背包问题)
(数学方法:1,2,...,2^m选一些数字相加,可以得出[0,2^(m+1)) #includeusing namespace std; int n,m,v[2001],w[2001],f[2001],l[2001]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d%d",&v[i],&w[i],&l[i]); } for(int i=1;i<=n;i++){ int res=l[i]; for(int k=1;k<=res;res-=k,k*=2)//切成l份 for(int j=m;j>=v[i]*k;j--){ f[j]=max(f[j],f[j-v[i]*k]+w[i]*k); } for(int j=m;j>=v[i]*res;j--)//多出来的,好比8=1+2+4+1,多出来一个1 f[j]=max(f[j],f[j-v[i]*res]+w[i]*res); } printf("%dn",f[m]); system("pause"); }
利用单调队列思想 #includeusing namespace std; int n,m,v,w,t,f[10001],c[10001][2]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i <= n; i++){ scanf("%d%d%d",&v,&w,&t); for(int j=0; j < v; j++){ int k=0,l=1; for(int p=j,x=1; p <= m;p+=v,++x){ int e=f[p]-x*w,r=x+t; for(; k >= l && c[k][0] <= e;--k); c[++k][0]=e;c[k][1]=r; f[p]=c[l][0]+w*x; for(; k >= l && c[l][1] == x; ++l); } } } printf("%dn",f[m]); system("pause"); }
分组背包(每个物品属于哪个组,且一个组只能拿一个)
#includeusing namespace std; int n,m,v[1001],w[1001],a[1001],f[1001]; vector c[1001]; int main(){ scanf("%d%d",&n,&m); for(int i=1; i <= n; i++){ scanf("%d%d%d",&a[i],&v[i],&w[i]); c[a[i]].push_back(i); } for(int i=1; i <= 1000; i++) for(int j=m; j > 0; j--){ for(auto k:c[i])//k是一个下标,枚举选这组里的几种选择 if(v[k]<=j) f[j]=max(f[j],f[j-v[k]]+w[k]); } printf("%dn",f[m]); system("pause"); }
二维背包(多了个体力这个限制条件(不一定是体力))
#includeusing namespace std; int n,m,v[1001],w[1001],t[1001],f[101][101],k; int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=1; i <= n; i++) scanf("%d%d%d",&v[i],&w[i],&t[i]); for(int i=1; i<=n; i++) for(int j=m; j >= v[i]; j--) for(int x=k; x >= t[i]; x--){ f[j][x]=max(f[j][x],f[j-v[i]][x-t[i]]+w[i]); } printf("%dn",f[m][k]); system("pause"); }
混合背包(就是01,完全,多重混在一起),由于多重的单调队列思想方法不是很好写,这边就用数学结论的方法
#includeusing namespace std; int n,m,k[10001],v[10001],w[10001],f[10001],l[10001]; int main(){ scanf("%d%d",&n,&m); for(int i=1; i <= n ;i++){ cin>>k[i]>>v[i]>>w[i]; if(k[i]==3){ cin>>l[i]; } } for(int i=1;i<=n;i++) { if(k[i]==2)//完全背包 { for(int j=v[i];j<=m;j++){ f[j]=max(f[j],f[j-v[i]]+w[i]); } } if(k[i]==1){ for(int j=m;j>=v[i];j--){ f[j]=max(f[j],f[j-v[i]]+w[i]); } } else//多重背包和01 { int res=l[i]; for(int p=1;p<=res;res-=p,p*=2)//切成l份 for(int j=m;j>=v[i]*p;j--){ f[j]=max(f[j],f[j-v[i]*p]+w[i]*p); } for(int j=m;j>=v[i]*res;j--)//多出来的,好比8=1+2+4+1,多出来一个1 f[j]=max(f[j],f[j-v[i]*res]+w[i]*res); } } cout<



