使用动态规划求解0-1背包问题

0-1背包问题

给定n个物品,每个物品有一定的质量,记为 m 1 , m 2 , . . . , m n m_1,m_2,...,m_n m1,m2,...,mn,同时每个物品又有一定的价值记为 v 1 , v 2 , . . . , v 3 v_1,v_2,...,v_3 v1,v2,...,v3
现在给定一个背包,其最大能承受的质量为 m a x W e i g h t maxWeight maxWeight
在不超过背包承重的情况下求出背包里面物品的价值的最大值。
使用数学语言描述就是:
求出序列 x = { x 1 , x 2 , . . . , x n } , x i ∈ { 0 , 1 } s . t . ∑ i = 1 n x i m i ≤ m a x W e i g h t m a x V a l u e s = m a x ∑ i = 1 n x i v i 求出序列x=\{x_1,x_2,...,x_n\},x_i\in\{0,1\}\\ s.t. \sum_{i=1}^nx_im_i\le maxWeight\\ maxValues=max \sum_{i=1}^nx_iv_i 求出序列x={x1,x2,...,xn},xi{0,1}s.t.i=1nximimaxWeightmaxValues=maxi=1nxivi

求解思路

求解背包问题有很多的思路,例如贪心算法,回溯算法,遗传算法,以及本文中将要讨论的动态规划算法。

动态规划

  1. 直接求解问题有时很复杂,但是可以通过求解子问题间接求解最后的问题(和递归类似)
  2. 使用动态规划的时候,通常可以求出相应的递归表达式

分析思路

从特殊到一般

假设有如下的物品,其重量和价值如下表所示

物品编号重量价值
142
234
323
434
53

其中背包的最大容量为7,则背包里面物品最大的价值分析

分析

按照动态规划的思想,关键是需要将复杂问题分解为一个或者几个子问题,通过求解子问题,然后求解出最终的问题。

将问题进行划分

假设当前已经确认了前4个物品在背包容量为7的情况下物品的最大价值 m a x V a l u e [ 4 , 7 ] maxValue[4,7] maxValue[4,7],此时考虑第五个物品,究竟要不要将其装入背包,分为如下几种情况:

m a x V a l u e [ i , j ] maxValue[i,j] maxValue[i,j]表示在前i个物品在背包容量为j的情况下,背包里面物品的总价值

  1. 如果第五个物品的重量直接大于背包的容量,第五个物品肯定是装不进去的,此时前5个物品在背包容量为7的情况下物品的最大价值就是前4个物品在背包容量为7的情况下物品的最大价值,即 m a x V a l u e [ 5 , 7 ] = m a x V a l u e [ 4 , 7 ] maxValue[5,7]=maxValue[4,7] maxValue[5,7]=maxValue[4,7]

  2. 如果第五个物品的重量小于背包的容量,则第五个物品可能放进去

    2.1 如果不放进去,此时背包里面物品的总价值为 m a x V a l u e [ 4 , 7 ] maxValue[4,7] maxValue[4,7];

    2.2 如果将物品放进背包,此时物品的总价值应当为: m a x V a l u e [ 4 , 7 − 第五个物品的质量 ] + 第五个物品的价值 maxValue[4,7-第五个物品的质量]+第五个物品的价值 maxValue[4,7第五个物品的质量]+第五个物品的价值

    2.3 在上述两种情况下求出物品总价值的最大值即可

则接下来的任务就是求解 m a x V a l u e [ 4 , 7 − 第五个物品的质量 ] maxValue[4,7-第五个物品的质量] maxValue[4,7第五个物品的质量],这样问题就变成了求解一个子问题

核心:写出递推表达式

已知:
给定n个物品,每个物品有一定的质量,记为 m 1 , m 2 , . . . , m n m_1,m_2,...,m_n m1,m2,...,mn
同时每个物品又有一定的价值记为 v 1 , v 2 , . . . , v 3 v_1,v_2,...,v_3 v1,v2,...,v3
现在给定一个背包,其最大能承受的质量为 m a x W e i g h t maxWeight maxWeight
则根据上面的分析,可以写出递归式如下:

根据递归式进行编码

void dynamicWithTwo()
{

//1. 接收用户数据的输入
    int n, maxWeight;
    printf("输入背包的容量和物品的个数,中间用空格区分\n");
    scanf("%d%d", &maxWeight, &n);
    int *weights = (int *)malloc(sizeof(int) * (n + 1)); // 物品质量数组,0号单元不使用
    int *values = (int *)malloc(sizeof(int) * (n + 1));  // 物品价值数组
    printf("输入数据格式:物品质量 价值\n");
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &weights[i], &values[i]); // 接收物品质量
        getchar();
    }

//2. 处理数据
    // 生成二维数组
    int **maxValues = (int **)malloc(sizeof(int *) * (n + 1));
    for (int i = 0; i <= n; i++)
    {
        maxValues[i] = (int *)malloc(sizeof(int) * (maxWeight + 1)); // 包含质量为0的情况
        for (int j = 0; j <= maxWeight; j++)
        {
            maxValues[i][j] = 0;
        }
    }
    // 开始填表
    /*
        maxValues[i][j]的含义为编号从[0,i]的物品当中,在背包容量为j的前提下的最大的价值
        递推公式为:
    */
    for (int i = 1; i <= n; i++)
    {

        for (int j = 1; j <= maxWeight; j++)
        {
            // 根据递推公式写出if-else分支
            if (j >=weights[i])
            {
                maxValues[i][j] = max(maxValues[i - 1][j], maxValues[i - 1][j - weights[i]] + values[i]);
            }
            else
            {
                maxValues[i][j] = maxValues[i - 1][j];
            }
        }
    }

    // 打印输出最后得到的结果
    printf("最后得到的结果为:\n");
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= maxWeight; j++)
        {
            printf("%d\t", maxValues[i][j]);
        }
        printf("\n");
    }
}

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值