0-1背包问题

0-1背包问题

背包问题(Knapsack Problem)

  问题描述:一个旅行者准备随身携带一个背包,可以放入背包的物品有 n n n种,物品 j j j的重量和价值分别为 w j , v j , j = 1 , 2 , … , n w_j,v_j,j=1,2,…,n wj,vj,j=1,2,,n,如果背包的最大重量限制是 b b b,怎样选择放入背包物品以使得背包的价值最大?

  这是一个组合优化问题,设 x j x_j xj表示装入背包的第 j j j种物品的数量,那么目标函数与约束条件是:
目标函数  m a x ∑ j = 1 n v j x j 约束条件  { ∑ j = 1 n w j x j ≤ b x j ∈ N \text{目标函数}\space max\sum_{j=1}^nv_jx_j\\ \text{约束条件}\space \begin{cases} \sum_{j=1}^nw_jx_j\le b\\ x_j\in \text{N} \end{cases} 目标函数 maxj=1nvjxj约束条件 {j=1nwjxjbxjN
  当 x j = 0 或 1 x_j=0或1 xj=01时的背包问题称之为0-1背包问题

  下面我们讨论其解法。

  这个问题是否能由转成子问题的解的组合呢?首先我们可以知道已知条件: n n n个已知重量与其价值的物品,以及背包的最大容量 b b b。假设最大价值为 F F F,对于一个物品而言,它要么装进背包,要么不装进背包。于是我们可以得到两种情况一种是一个物品装进背包以及其不装进,比较其大小就得到了最大价值。当然如果背包的容量已经装不下该物品,则可以认为最优解是不装该物品。

  我们假设这个物品是最后一个物品。于是得到了以下的递推形式:
F ( n , b ) = { m a x { F ( n − 1 , b ) , F ( n − 1 , b − w n ) + v n }   b ≥ w n F ( n − 1 , b ) F(n,b)=\begin{cases}max\{F(n-1,b),F(n-1,b-w_n)+v_n\} \space b\ge w_n\\ F(n-1,b) \end{cases} F(n,b)={max{F(n1,b),F(n1,bwn)+vn} bwnF(n1,b)
可知这个问题满足最优子结构的特征。可以使用动态规划求解。为了保存最大价值我们用一个二维数组来保存对应的 F ( n , b ) F(n,b) F(n,b),为了构造最优解我们通过一个二维数组来保存每次的比较结果 S ( n , b ) S(n,b) S(n,b),其定义如下:
S ( n , b ) = { 1      b ≥ w n 且 F ( n − 1 , b ) ≤ F ( n − 1 , b − w n ) + v n 0      其 他 S(n,b) = \begin{cases} 1\space\space\space\space b\ge w_n且F(n-1,b)\le F(n-1,b-w_n)+v_n\\ 0\space\space\space\space 其他 \end{cases} S(n,b)={1    bwnF(n1,b)F(n1,bwn)+vn0    
我们通过 S ( n , b ) S(n,b) S(n,b)就能构造出最优解。

具体伪代码如下:

MakeBest(S,n,b,w)
	ans[1……n] = {0,……,0}//最优解
	while(b>=0&&n>=0)
		ans[n] = S(n,b)
        b-=ans[n]*W[n]
        n--;
	return ans
/**
 * 背包问题
 * */

#include <iostream>
#include <vector>
using namespace std;

pair<int,vector<int>> KP(vector<int> &w,vector<int> &v,int n,int b){
    vector<vector<int>> F(b+1,vector<int>(n+1,0));
    vector<vector<int>> S(b+1,vector<int>(n+1,0));
    
    vector<int> ans(n,0);

    for (int i = 1; i < b+1; i++)
    {
        for (int j = 1; j < n+1; j++)
        {
            if(i-w[j-1]<0) {F[i][j] = F[i][j-1];continue;}
            if(F[i][j-1]<F[i-w[j-1]][j-1]+v[j-1]){
                F[i][j] = F[i-w[j-1]][j-1]+v[j-1];
                S[i][j] = 1;
            }
            else F[i][j] = F[i][j-1];
        }
        
    }
    int ANS = F[b][n];

    //打印中间过程
    for (int i = 1; i < n+1; i++)
    {
        for (int j = 1; j < b+1; j++)
        {
            cout<<F[j][i]<<",";
        }
        cout<<endl;
    }
    
    //构造最优解
    while(n>=1&&b>=1){
        ans[n-1] = S[b][n];
        b-=ans[n-1]*w[n-1];
        n--;
    }
    return make_pair(ANS,ans);

} 
int main(){
    int n = 4,b = 10;
    vector<int> w = {2,3,4,7};
    vector<int> v = {1,3,5,9};
    
    auto ans = KP(w,v,n,b);
    cout<<ans.first<<endl;
    for(auto x : ans.second){
        cout<<x<<",";
    }
    system("pause");
}

Reference

《算法设计与分析》第二版 屈婉玲,第三章3.3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值