背包九讲 整理

Posted iuk11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包九讲 整理相关的知识,希望对你有一定的参考价值。

该篇文章为了自己回顾基础,也为了下学期为社团讲解做个铺垫,本来想做ppt但是公式真是太折磨人了。
主要是自己太菜了,想一直刷题,发现到后面想不出来,看题解也要看半天,仔细回想觉得是因为经典问题不牢固的原因。
所有解题思路学习来源于acwing。


01背包问题

问题叙述:
        有 n n n 件物品和一个容量为 v v v 的背包,每件物品只能选择一次。
        对于第 i i i 件物品,体积为 v [ i ] v[i] v[i] ,价值为 w [ i ] w[i] w[i]
        问不超过背包容量的情况下,拿哪些物品得到的总价值最大。
问题分析:
        采用闫式dp分析法,在集合的角度进行分析 (状态表示: d p [ i ] [ j ] dp[i][j] dp[i][j] )
        集合:只考虑前 i i i 个物品在容量为 j j j 的背包下,拿到的最大价值。
        属性:Max.
        集合划分:如图

得到状态转移方程: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v ] + w ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w) dp[i][j]=max(dp[i1][j],dp[i1][jv]+w)
完整代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v,w;
int dp[1010][1010];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w;
        for(int j=0;j<=m;j++){
            dp[i][j]=dp[i-1][j];
            if(j-v>=0) dp[i][j]=max(dp[i][j],dp[i-1][j-v]+w);
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

优化空间:
        由上文分析可知,所有的 [ i ] [i] [i] 都是由 [ i − 1 ] [i-1] [i1] 转移来的。那么可以用滚动数组代替第一维 or 可以把体积由大到小枚举,这样在判断的时候当前物品的时候, [ i ] [i] [i]中的状态还没有更新,相当于 [ i − 1 ] [i-1] [i1]

//滚动数组方法
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v,w;
int dp[2][1010];
int id;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w;
        id^=1;
        for(int j=0;j<=m;j++){
            dp[id][j]=dp[id^1][j];
            if(j-v>=0) dp[id][j]=max(dp[id][j],dp[id^1][j-v]+w);
        }
    }
    cout<<dp[id][m]<<endl;
    return 0;
}
//一维数组写法
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v,w;
int dp[1010];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w;
        for(int j=m;j>=v;j--){
            dp[j]=max(dp[j],dp[j-v]+w);
        }
    }
    cout<<dp[m]<<endl;
    return 0;
}

完全背包问题

问题叙述:
        有 n n n 件物品和一个容量为 v v v 的背包,每件物品可以选择无数次。
        对于第 i i i 件物品,体积为 v [ i ] v[i] v[i] ,价值为 w [ i ] w[i] w[i]
        问不超过背包容量的情况下,拿哪些物品得到的总价值最大。
问题分析:
        采用闫式dp分析法,在集合的角度进行分析 (状态表示: d p [ i ] [ j ] dp[i][j] dp[i][j] )
        集合:只考虑前 i i i 个物品在容量为 j j j 的背包下,拿到的最大价值。
        属性:Max.
        集合划分:如图

更正:不是 k 种选法,是 k*v 不大于 j 种选法
得到状态转移方程: d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ∗ v ] + k ∗ w } dp[i][j]=max\\{dp[i-1][j-k*v]+k*w\\} dp[i][j]=max{dp[i1][jkv]+kw} k = 0 , 1 , 2... k=0,1,2... k=0,1,2...

//三维写法 时间复杂度O(n^3)
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v,w;
int dp[1010][1010];
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w;
        for(int j=0;j<=m;j++){
            for(int k=0;k*v<=j;k++){
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*v]+k*w);
            }
        }
    }
    cout<<dp[n][m];
    return 0;
}

因为上一个写法的时间复杂度太大,我们对状态转移方程再次进行整理,得到: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − v ] + w , d p [ i − 1 ] [ j − 2 ∗ v ] + 2 ∗ w + d p [ i − 1 ] [ j − 3 ∗ v ] + 3 ∗ w . . . ) dp[i][j]=max(dp[i-1][j-v]+w,dp[i-1][j-2*v]+2*w+dp[i-1][j-3*v]+3*w...) dp[i][j]=max(dp[i1][jv]+w,dp[i1][j2v]+2w+dp[i1][j3v]+3w...) d p [ i ] [ j − v ] = m a x ( d p [ i − 1 ] [ j − 2 ∗ v ] + w , d p [ i − 1 ] [ j − 3 ∗ v ] + 2 ∗ w . . . ) dp[i][j-v]=max(dp[i-1][j-2*v]+w,dp[i-1][j-3*v]+2*w...) dp[i][jv]=max(dp[i1][j2v]+w,dp[i1][j3v]+2w...)由两公式得到: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − v ] + w ) dp[i][j]=max(dp[i-1][j],dp[i][j-v]+w) dp[i][j]=max(dp[i1][j],dp[i][jv]+w)解释: d p [ i ] [ j ] dp[i][j] dp[i][j] 的最大值为不选或者选一个的最大值,因为选一个的最大值,就是选一个或者选两个的最大值…

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v,w;
int dp[1010][1010];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>v>>w;
        for(int j=0;j<=m;j++){
            dp[i][j]=dp[i-1][j];
            if(j-v>=0) dp[i][j]=max(dp[i-1][j],dp[i][j-v]+w);
        }
    }
    cout<<dp[n][m];
    return 0;
}

最后再优化一下空间:
对于为什么dp[j]可以表示 [ i − 1 ] [i-1] [i1]的状态,可以思考一下,从小到大枚举的意义。

#include以上是关于背包九讲 整理的主要内容,如果未能解决你的问题,请参考以下文章

记录B站yxc的背包九讲相关代码

背包九讲

背包九讲

动态规划 之背包问题(九讲)

背包九讲

背包九讲(转载,实在不知道哪个是原创了)