背包问题

0-1背包

背包容量C,物品代价为w[i] 价值为v[i],所有物品只可使用一次,求最大收益.

f(i,c) 代表使用物品 [0,…,i] 填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = f(i-1,c-w[i])+v[i]
和上一行的进行比较
因此有: f(i,c)=max( f(i,c) , f(i-1,c-w[i])+v[i] )

dp[j] 就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,…,i]
0-1背包问题需要 逆序计算 ,因为每个状态都和之前的状态相关
初始化问题:对于收益来说本身的初始值就是0,在构造dp时已经初始化了

/*
    f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
    第i件物品可以选择用或者不用
    1.不用:   f(i,c) = f(i-1,c)
    2.用:     f(i,c) = f(i-1,c-w[i])+v[i]
        和上一行的进行比较
    因此有:f(i,c)=max(   f(i,c) ,  f(i-1,c-w[i])+v[i]  )

    dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
    "当前"就是外层循环遍历物品得到[0,...,i]

    0-1背包问题需要逆序计算,因为每个状态都和之前的状态相关

    初始化问题:对于收益来说本身的初始值就是0,在构造dp时已经初始化了
*/
int testFor01(vector<int>& v,vector<int>& w,int c)
{
    vector<int> dp(c+1,0);
    /*初始化一位数组:仅使用第一件物品时的状态变化过程*/
    // for(int j=0; j<=c; ++j){
    //     dp[j] = j>=w[0]?v[0]:0;
    // }
    /*开始增大使用范围:*/
    for(int i=0; i<n; ++i){
        /*当前容量需要大于该物品,才能继续用*/
        for(int j=c; j>=w[i]; --j){     
            dp[j] = max(dp[j], v[i]+dp[j-w[i]]);
        }
    }
    return dp[c];
}

完全背包

背包容量C,物品代价为w[i] 价值为v[i],所有物品可无限使用,求最大收益.

f(i,c)代表使用物品[0,…,i]填充容量c所获得的最大收益
第i件物品可以选择用或者不用
1.不用: f(i,c) = f(i-1,c)
2.用: f(i,c) = max( f(i,c-k*w[i])+k*v[i] ) 0<=k<= c/w[i]
注意:这里和0-1背包的区别:
从左向右依次计算使用0,1,2,…c/w[i]个物品i所获的收益,
和本行的左边进行比较
因此有:f(i,c)=max( f(i,c) , f(i,c-w[i])+v[i] )

dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
"当前"就是外层循环遍历物品得到[0,…,i]

完全背包问题需要**顺序计算**,顺序会覆盖以前的状态,达到选择多次的情况

/*
    f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
    第i件物品可以选择用或者不用
    1.不用:   f(i,c) = f(i-1,c)
    2.用:     f(i,c) = max(  f(i,c-k*w[i])+k*v[i]  )   0<=k<=c/w[i]
        注意:这里和0-1背包的区别:
        从左向右依次计算使用0,1,2,..c/w[i]个物品i所获的收益,
        和本行的左边进行比较
    因此有:f(i,c)=max(   f(i,c) ,  f(i,c-w[i])+v[i]  )

    dp[j]就代表使用"当前"的物品,填充容量j,所获得的最大收益.
    "当前"就是外层循环遍历物品得到[0,...,i]

    完全背包问题需要顺序计算,顺序会覆盖以前的状态,达到选择多次的情况
*/
int testForFull(vector<int>& v,vector<int>& w,int c)
{
    /*完全背包:顺序计算*/
    vector<int> dp(c+1,0);
    for(int i=0; i<v.size(); ++i){
        for(int j=w[i]; j<=c; ++j){
            dp[j] = max(dp[j],v[i]+dp[j-w[i]]);
        }
    }
    return dp[c];
}

多重背包

背包容量C,物品代价为w[i] 价值为v[i],所有物品均有个数限制,求最大收益.

/*
    多重背包类似于完全背包,只是k的范围变小了
    f(i,c)代表使用物品[0,...,i]填充容量c所获得的最大收益
    第i件物品可以选择用或者不用
    1.不用:   f(i,c) = f(i-1,c)
    2.用:     f(i,c) = max(  f(i,c-k*w[i])+k*v[i]  )   0<=k<=n[i]

    优化:转化为0-1背包(加上二分法)
*/
int testForMulti(vector<int>& v,vector<int>& w,vector<int> n,int c)
{
    /*新的背包,物品*/
    vector<int> vArr, wArr;
    /*拆分物品*/
    for(int i=0; i<v.size(); ++i){
        int k=1;
        while(n[i]-k>0){
            n[i]-=k;
            vArr.push_back(k*v[i]);
            wArr.push_back(k*w[i]);
            k*=2;
        }
        /*该物品剩余的部分合并后插入*/
        vArr.push_back(n[i]*v[i]);
        wArr.push_back(n[i]*w[i]);
    }
    /*新的0-1背包问题*/
    vector<int> dp(c+1,0);
    for(int i=0; i<vArr.size(); ++i){
        for(int j=c; j>=wArr[i]; --j){     
            dp[j] = max(dp[j], vArr[i]+dp[j-wArr[i]]);
        }
    }
    return dp[c];
}

二维背包

背包体积A,重量B,
物品体积a[i],重量b[i],价值v[i],求最大收益.
具体顺序计算还是逆序计算取决于本质是"0-1"还是"完全"背包

/*
    相比于0-1背包只是多了一维限制,只需要多加一层内循环即可
    f(i,A,B)代表使用物品[0,...,i]填充容量A,B所获得的最大收益
    第i件物品可以选择用或者不用
    1.不用:   f(i,A,B) = f(i-1,A,B)
    2.用:     f(i,A,B) = f(i-1,A-a[i],B-a[i])+v[i]
    因此有:f(i,A,B)=max(   f(i,A,B) ,  f(i-1,A-a[i],B-a[i])+v[i]  )

    dp[row][col]就代表使用"当前"的物品,填充容量row,col,所获得的最大收益.
*/
int testFor2D(int A,int B,vector<int>& a,vector<int>& b,vector<int>& v)
{
    /*此处按照逆序计算*/
    vector<vector<int>> dp(A+1,vector<int>(B+1,0));
    for(int i=0; i<N; ++i){
        for(int row=A; row>=a[i]; --row){
            for(int col=B; col>=b[i]; --col){
                dp[row][col]=max(dp[row][col],dp[row-a[i]][col-b[i]]+v[i]);
            }
        }
    }
    return dp[A][B];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值