题目来源
01背包
朴素算法就是列状态转移,对于第i个物品选或不选,不选则最大值是前i-1个物品的最大值,选则体积减去对应物品体积,价值取两种可能情况的最大值,即
dp[i][j]=max(dp[i-1][j],dp[i-1][j-vi[i]]+wi[i])
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int vi[1001],wi[1001];
int dp[1001][1001];
int main()
{
int N,V;
cin>>N>>V;
for (int i = 1; i <= N; i ++ ){
cin>>vi[i]>>wi[i];
}
for(int i=1;i<=N;i++){
for(int j=0;j<=V;j++){
dp[i][j]=dp[i-1][j];
if(j>=vi[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-vi[i]]+wi[i]);
}
}
cout<<dp[N][V]<<endl;
}
考虑到只与i-1的状态有关,利用滚动数组;j和j-v[i]变量只有1个数,所以从大到小遍历,这样的话j-vi[i]的值不会被提前更新,还是i-1时的值
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int vi[1001],wi[1001];
int dp[1001];
int main()
{
int N,V;
cin>>N>>V;
for (int i = 1; i <= N; i ++ ){
cin>>vi[i]>>wi[i];
}
for(int i=1;i<=N;i++){
for(int j=V;j>=vi[i];j--){
dp[j]=max(dp[j],dp[j-vi[i]]+wi[i]);
}
}
cout<<dp[V]<<endl;
}
完全背包
完全背包与01背包不同之处在于相同物品可以选无限多次,所以状态转移方程为
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w,dp[i-1][j-2v]+2w+……);
稍作换元
dp[i][j-v]=max(dp[i-1][j-v],dp[i-1][j-2v]+w,dp[i-1][j-3v]+2w+……)+w;
所以可得
dp[i][j]=max(dp[i-1][j],dp[i][j-v]+w);
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int vi[1001],wi[1001];
int dp[1001];
int main()
{
int N,V;
cin>>N>>V;
for (int i = 1; i <= N; i ++ ){
cin>>vi[i]>>wi[i];
}
for(int i=1;i<=N;i++){
for(int j=vi[i];j<=V;j++){
dp[j]=max(dp[j],dp[j-vi[i]]+wi[i]);
}
}
cout<<dp[V]<<endl;
}
多重背包I
与01背包区别在于每个物品可以最多选s个,所以在数据范围在合理时,我们可以直接使用暴力遍历求解,状态转移方程如下:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w,dp[i-1][j-2v]+2w+……);
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[101];
int main()
{
int N,V;
cin>>N>>V;
int v,w,s;
for (int i = 1; i <= N; i ++ ){
cin>>v>>w>>s;
for (int j = V; j >= v; j -- ){
for(int k=0;k<=s && k*v<=j;k++){
dp[j]=max(dp[j],dp[j-k*v]+k*w);
}
}
}
cout<<dp[V]<<endl;
}
多重背包II
如果数据量稍微大一点,可以采用二进制表示状态方法进行优化,例如表示1-7,我们事实上只需要1,2,4这3个数排列组合就可以得到,所以捆绑好,对这3个数选或者不选,实现最终的所以状态转移方程为:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w);
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int dp[2001];
struct Good{
int v,w;
};
int main()
{
int N,V;
cin>>N>>V;
int v,w,s;
vector<Good> goods;
for (int i = 1; i <= N; i ++ ){
cin>>v>>w>>s;
for (int k=1;k<=s;k*=2){
s-=k;
goods.push_back({k*v,k*w});
}
if(s>0) goods.push_back({s*v,s*w});
}
for (auto good:goods){
for (int j = V; j >=good.v; j -- ){
dp[j]=max(dp[j],dp[j-good.v]+good.w);
}
}
cout<<dp[V]<<endl;
}
多重背包III
AcWing 6. 多重背包问题 III【单调队列优化+图示】 - AcWing
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20001;
int dp[N];
int q[N],pre[N];
int main()
{
int n,m;
cin>>n>>m;
for (int i = 0; i < n; i ++ ){
int v,w,s;
cin>>v>>w>>s;
memcpy(pre,dp,sizeof(dp));
for (int j = 0; j < v ; j ++ ){
int head=0,tail=-1;
for(int k=j;k<=m;k+=v){
if(head<=tail && k-q[head]>s*v) head++;//如果队列长度大了,头++
while(head<=tail && pre[q[tail]]-(q[tail]-j)/v*w<=pre[k]-(k-j)/v*w) tail--;//如果队列末尾数值大小不大于j+k*v对应的数,更新
if(head<=tail) dp[k]=max(dp[k],pre[q[head]]+(k-q[head])/v*w);//对k作状态转移,得到下标为j+k*v对应的最大值
q[++tail]=k;//新加入队列
}
}
}
cout<<dp[m]<<endl;
}
混合背包
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int dp[1001];
struct Good{
int v,w;
};
int main()
{
int n,m;
cin>>n>>m;
for (int i = 0; i < n; i ++ ){
int v,w,s;
cin>>v>>w>>s;
if(s==-1){
for (int j = m; j >= v ; j -- ){
dp[j]=max(dp[j],dp[j-v]+w);
}
}else if(s==0){
for (int j = v; j <= m ; j ++ ){
dp[j]=max(dp[j],dp[j-v]+w);
}
}else if(s>0){
vector<Good> goods;
for(int k=1;k<=s;k*=2){
s-=k;
goods.push_back({v*k,w*k});
}
if(s>0) goods.push_back({s*v,s*w});
for(auto good:goods){
for(int j=m;j>=good.v;j--){
dp[j]=max(dp[j],dp[j-good.v]+good.w);
}
}
}
}
cout << dp[m]<<endl;
}
二维背包费用
与01背包类似,只是多加一维限制,状态转移方程为:
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-vi[i]][k-mi[i]]+wi[i])
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[101][101];
int main()
{
int N,V,M;
cin>>N>>V>>M;
int v,w,m;
for (int i = 1; i <= N; i ++ ){
cin>>v>>m>>w;
for(int j=V;j>=v;j--){
for(int k=M;k>=m;k--){
dp[j][k]=max(dp[j][k],dp[j-v][k-m]+w);
}
}
}
cout<<dp[V][M]<<endl;
}
分组背包
相当于在组内的值并列进入动态规划中,因为是同一轮判断的,所以对应一个j不会出现组内值同时出现的情况。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 110;
int s[maxn],v[maxn][maxn],w[maxn][maxn];
int dp[maxn];
int main()
{
int N,V;
cin>>N>>V;
for (int i = 0; i < N; i ++ ){
cin>>s[i];
for(int j=0;j<s[i];j++){
cin>>v[i][j]>>w[i][j];
}
}
for(int i=0;i<N;i++){
for(int j=V;j>=0;j--){
for(int k=0;k<s[i];k++){
if(j>=v[i][k]) dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
}
}
}
cout<<dp[V]<<endl;
}
有依赖背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int f[110][110];
vector<int> g[110];
int v[110],w[110];
int n,m,root;
int dfs(int x){
for(int i=v[x];i<=m;i++) f[x][i]=w[x];//x一定要选,选上
for(int i=0;i<g[x].size();i++){//遍历子树
int y=g[x][i];
dfs(y);
for(int j=m;j>=v[x];j--){//只选一次
for(int k=0;k<=j-v[x];k++){//组内挑选
f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
int fa;
cin>>v[i]>>w[i]>>fa;
if(fa==-1) root=i;
else g[fa].push_back(i);
}
dfs(root);
cout<<f[root][m];
}
背包问题求方案数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1001;
const int mod=1e9+7;
int dp[N],cnt[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<=m;i++) cnt[i]=1;//啥也没有也是一种方案
for(int i=0;i<n;i++){
int v,w;
cin>>v>>w;
for(int j=m;j>=v;j--){
int s=dp[j-v]+w;
if(s>dp[j]){//小了就更新
dp[j]=s;
cnt[j]=cnt[j-v];
}else if(s==dp[j]){
cnt[j]=(cnt[j]+cnt[j-v])%mod;//相等就累加
}
}
}
cout<<cnt[m]<<endl;
}
背包问题求方案
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;//数据范围稍微开大点不然报错
int dp[N][N],v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=n;i>=1;i--){
for(int j=0;j<=m;j++){
dp[i][j]=dp[i+1][j];
if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i+1][j-v[i]]+w[i]);
}
}
int cur_v=m;
for(int i=1;i<=n;i++){
if(cur_v<=0) break;
if(cur_v>=v[i] && dp[i][cur_v]==dp[i+1][cur_v-v[i]]+w[i]){
cout<<i<<' ';
cur_v-=v[i];
}
}
}