0-1背包问题

问题描述:
给出背包的容积,商品的价格和商品的体积,在不超过背包容量的情况下,买走价格最大的商品。
(所谓0-1,就是每件商品最多只能选一次,所以这是个很基础的动态规划问题)
商品如下:在这里插入图片描述
方法1:回溯算法
该算法基于以递归的方式建立的枚举算法。
枚举递归的基本思想:要求剩余i件商品,背包剩余容积为c时的最大价格(最优解),只需要求(剩余i-1件商品,背包剩余容积为c时的最优解)和{剩余i-1件商品,背包剩余容积为(c-第i件商品的体积)的最优解加上第i件商品的价格}的最大值。 因为剩余i-1种商品有两种情况:
1·选了第i种商品
2·没有选第i种商品

按照这样分解下去就能把所有情况都能考虑
图解:
在这里插入图片描述

方法二:递推求解算法
回溯算法的缺点是要递推后还得递归,有点费时,我们发现,不用递归,可以直接递推求解。

由于best[i][c]是依靠best[i-1][c]和best[i-1][c-V[i]]+P[i]比较求出,,又为从小到大求解best,所以best[i][c]表要填满,才能确保后面得情况能被顺利求出来
best[i][c]求得结果如图
在这里插入图片描述
从表中可知,i=5,c=13的最优解为28

下面分析is_sel表格,即在剩余i件物品,容量为c的情况下,第i件商品有没有选择的表格。
在这里插入图片描述
如何分析选了哪种商品?
我们要分析先分析在i=5,c=13时是否选了第五种商品
如图可知在i=5,c=13时选了第5种商品–牛奶
再分析在i=4,c=13-4=9的时候是否选了第4种商品
如图可知在i=4,c=9时选了第4种商品–面包
再分析在i=3,c=9-5=4的时候是否选了第3种商品
如图知没有在i=3,c=4的时候选第3种商品
则分析在i=2,c=4的时候是否选了第3种商品
如图可知在i=2,c=4时选了第2种商品–饼干
c-4=0;结束。

(这种方法只能选择一种情况,要是在i=某个数时,既可以选这个商品,也可以不选这个商品,这就不能顾及周全)

#include<iostream>
using namespace std;

#define   MIN_INF   -2147483648

// 各种商品的价格,从1开始
int P[6] = { 0,24,2,9,10,9 };

// 上面几种商品对应的体积,从1开始
int V[6] = { 0,10,3,4,5,4 };

// 记录是否在背包剩余该容积情况下选择了该商品(初始化为0,表示没选择)
int is_sel[6][14] = { 0 };     // 容量从0到13,所以,二维数组第二维大小为14

//  记录第剩余容积c时,剩余i个商品的最优解,做到记忆化搜索,防止重复浪费时间,默认初始化为0;
int best[6][14];




// 直接用递推的方法求最优解
int KnapsackSR(int I, int C)
{
    // 求前i个商品在剩余体积为c的时候的最优解,这里与回溯算法不同的是这个每个c都要考虑到
    for(int i=1;i<=I;++i)
        for (int c= 0; c <= C; ++c)      // 这个方法c大于0,不会出现(c<0)的情况
        {
             // 剩余容积比第i件商品的体积大才有可能会选择第i件商品
            if (V[i] <= c)
            {
                //判断该不该选第i种商品
                if (best[i - 1][c - V[i]] + P[i] > best[i - 1][c])
                {
                    best[i][c] = best[i - 1][c - V[i]] + P[i];
                    is_sel[i][c] = 1;
                }
                else
                {
                    best[i][c] = best[i - 1][c];
                    is_sel[i][c] = 0;
                }
            }
            // 要是剩余的容积比第i件商品的体积都小,则第i件商品不能选
            else
            {
                best[i][c] = 0;
            }
        }
    return best[I][C];
}
void show_seletion()
{
    int volume = 13;      // 记录总容积

    // 从[5][13]的情况开始找
    for (int i = 5; i >= 1; --i)
    {
        // 若第i件商品被选了,则输出,并把剩余的容积减去该商品的体积
        if (is_sel[i][volume]==1)        
        {
            volume -= V[i];        // 减去该物品所占的体积的容积

            // 观察选的是什么
            switch (i)
            {
            case 1:cout << "选了一瓶可乐 "; break;
            case 2:cout << "选了一瓶汽水 "; break;
            case 3:cout << "选了一袋饼干 "; break;
            case 4:cout << "选了一袋面包 "; break;
            case 5:cout << "选了一盒牛奶 "; break;
            }
        }
    }
    cout << endl;
}

int main()
{
    // 将记忆初始化为-1
    memset(best, -1, sizeof(best));
    int ans=KnapsackSR(5,13);
    show_seletion();
    cout << "共" << ans << "元" << endl;
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值