动态规划背包问题优化模板(完整代码)

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]]),与状态转移方程不符。

#include<bits/stdc++.h>
using 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("%d\n",f[m]);
}

完全背包(每个物品可取多次)

#include<bits/stdc++.h>
using 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("%d\n",f[m]);
    system("pause");
}

多重背包(规定一个物品可以取多次,转换成01背包问题)

(数学方法:1,2,...,2^m选一些数字相加,可以得出[0,2^(m+1))
#include<bits/stdc++.h>
using 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("%d\n",f[m]);
    system("pause");
}
利用单调队列思想
#include<bits/stdc++.h>
using 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("%d\n",f[m]);
    system("pause");
}

分组背包(每个物品属于哪个组,且一个组只能拿一个)

#include<bits/stdc++.h>
using namespace std;
int n,m,v[1001],w[1001],a[1001],f[1001];
vector<int> 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("%d\n",f[m]);
    system("pause");
}

二维背包(多了个体力这个限制条件(不一定是体力))

#include<bits/stdc++.h>
using 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("%d\n",f[m][k]);
    system("pause");
}

混合背包(就是01,完全,多重混在一起),由于多重的单调队列思想方法不是很好写,这边就用数学结论的方法

#include<bits/stdc++.h>
using 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<<f[m]<<"\n";
    system("pause");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值