动态规划入门篇--背包九讲(前三讲)

说到DP想必大家都非常头疼,今天就来说说dp的入门问题背包DP(dp最简单一类)。通过背包dp,你能理解动态规划中最经典的状态转移的思想,学好背包绝对会为征服DP迈下最坚实的一步。好话不多说,我们来讲讲第一个问题。(如果想检验自己的代码请移步至acwing)

01背包

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

咱们先上代码再说

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;
int f[N];
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,v;
    cin>>n>>v;
    for(int i=0;i<n;i++)
    {
        int c,w;
        cin>>c>>w;
        for(int j=v;j>=c;j--) f[j]=max(f[j],f[j-c]+w);
    }
    cout<<f[v]<<endl;
    return 0;
}

我们可以看到代码非常的简洁,我们遍历每一次的决策,对于第i个物品,是选还是不选,保存最大的方案继续向下进行,我们可以看到我们是从最大体积到最小体积进行枚举的,这是为什么呢?(我实在解释不清,移步至DD大牛),咱们可以用模拟法,把每个状态打印下来就好理解了(我是这样理解的)

完全背包

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
先上代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int f[N];
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,v;
    cin>>n>>v;
    for(int i=0;i<n;i++)
    {
        int c,w;
        cin>>c>>w;
        for(int j=c;j<=v;j++) f[j]=max(f[j],f[j-c]+w);
    }
    cout<<f[v]<<endl;
    return 0;
}

依然是用穷举法理解,我们可以看到完全背包与01背包在代码上的唯一区别就是枚举从从大到小改为从小到大。

多重背包(二进制优化)

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

直接上代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int f[N];
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,v;
    cin>>n>>v;
    for(int i=0;i<n;i++)
    {
        int c,w,s;
        cin>>c>>w>>s;
        for(int j=1;j<=s;j<<=1)
        {
            s-=j;
            for(int k=v;k>=j*c;k--)
                f[k]=max(f[k],f[k-j*c]+j*w);
        }
        if(s)
            for(int k=v;k>=s*c;k--)
                f[k]=max(f[k],f[k-s*c]+s*w);
    }
    cout<<f[v]<<endl;
    return 0;
}

这题我们重点讲解,对于多重背包有个很暴力的想法,我们将所有情况转化为01背包,直接求解,这就启发了我们用二进制优化,打个比方把:
比如说我们的s=10 ,10个体积为c,重量为w的物体我们能将其优化成4个物体:
1.(c,w)
2.(2c,2w)
3.(4c,4w)
4.(3c,3w)
只要你懂一点二进制,用1、2、3我们可以完美的表示7个体积为c,w的物体,再补上一个4即可。如果你没有学过二进制,请回去补一补请一头撞死 ,如果学过二级制再好好想一想一定能想出来的,如果想不出来,就再想一想请一头撞死
最后我想给大家分享一下多重背包的极限优化(单调队列优化),他能把时间复杂度优化为线性的。具体的做法和单调队列做法一样,将超过生命周期的数出队,以及将不满足单调性的队列中的元素剔除掉像极了现再的我,比我小的人比我NB咱们给出代码就不细讲了。因为正常题目二进制优化就够了,只要出题人有一点良知~~

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=20020;
int q[N];
int dp[N],g[N];
int tt,hh;
int main(){
    int n,v;
    cin>>n>>v;
    for(int i=0;i<n;i++)
    {
        int c,w,s;
        cin>>c>>w>>s;
        memcpy(g,dp,sizeof g);
        for(int j=0;j<c;j++)
        {
            tt=-1,hh=0;
            for(int k=j;k<=v;k+=c)
            {
                while(tt>=hh&&k-q[hh]>s*c) hh++;
                if(tt>=hh) dp[k]=max(dp[k],g[q[hh]]+(k-q[hh])/c*w);
                while(tt>=hh&&g[k]-(k-j)/c*w>g[q[tt]]-(q[tt]-j)/c*w) tt--;
                q[++tt]=k;
            }
        }
    }
    cout<<dp[v]<<endl;
    
    
    
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值