iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言动态规划之背包问题详解
  • 477
分享到

C语言动态规划之背包问题详解

2024-04-02 19:04:59 477人浏览 八月长安
摘要

01背包问题        给定n种物品,和一个容量为C的背包,物品i的重量是w[i],其价值为v[i]。问如何选择装入背包的物品,使得装入背

01背包问题

       给定n种物品,和一个容量为C的背包,物品i的重量是w[i],其价值为v[i]。问如何选择装入背包的物品,使得装入背包中的总价值最大?(面对每个武平,只能有选择拿取或者不拿两种选择,不能选择装入某物品的一部分,也不能装入物品多次)

  • 声明一个数组f[n][c]的二维数组,f[i][j]表示在面对第i件物品,且背包容量为j时所能获得的最大价值。
  • 根据题目要求进行打表查找相关的边界和规律
  • 根据打表列写相关的状态转移方程
  • 用程序实现状态转移方程

真题演练:

       一个旅行者有一个最多能装M公斤的背包,现在有n件物品,它们的重量分别是W1、W2、W3、W4、…、Wn。它们的价值分别是C1、C3、C2、…、Cn,求旅行者能获得最大价值。

输入描述:

       第一行:两个整数,M(背包容量,M<= 200)和N(物品数量,N<=30);
       第2…N+1行:每行两个整数Wi,Ci,表示每个物品的质量与价值。

输出描述:

       仅一行,一个数,表示最大总价值

样例:

输入:
10 4
2 1
3 3
4 5
7 9
输出:
12

解题步骤

定义一个数组dp[i][j]表示容量为j时,拿第i个物品时所能获取的最大价值。

按照题目要求进行打表,列出对应的dp表。

W[i](质量) V[i](价值) 0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0
2 1 0 0 1 1 1 1 1 1 1 1 1
3 3 0 0 1 3 3 4 4 4 4 4 4
4 5 0 0 1 3 5 5 6 8 8 9 9
7 9 0 0 1 3 5 5 6 9 9 10 12

       对于一个动态规划问题设置下标时最好从0开始,因为动态规划经常会和上一个状态有关系!从上面的dp表可以看出来对于一个物品我们拿还是不难需要进行两步来判断。第一步:判断背包当前的容量j是否大于物品当前的质量,如果物品的质量大于背包的容量那么就舍弃。第二步:如果背包可以装下这个物品,就需要判断装下该物品获取的最大价值是不是大于不装下这个物品所获取的最大价值,如果大于那么就把东西装下!根据这样的思想我们可以得到状态转移方程:

如果单签背包的容量可以装下物品:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
如果当前背包的容量装不下该物品:
dp[i][j]=dp[i-1][j];


#include <stdio.h>
int max(const int a,const int b)
{
    return a>b ? a:b;
}
int main()
{
    int w[35]={0},v[35]={0},dp[35][210]={0};
    int n,m;
    scanf("%d %d",&m,&n);
    int i,j;
    for(i=1;i<=n;i++){
        scanf("%d %d",&w[i],&v[i]);
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            if(j>=w[i])//如果当前背包的容量大于商品的质量
            {
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//判断是否应该拿下
            }
            else//大于背包的当前容量
            {
                dp[i][j]=dp[i-1][j];
            }
        }
    }
    for(int k=0;k<=n;k++)
    {
        for(int l=0;l<=m;l++)
        {
            printf("%d ",dp[k][l]);
        }
        printf("\n");
    }
    printf("%d\n",dp[n][m]);
}

在这里插入图片描述

       通过运行以上程序可以看到最终的输出dp表和我们的预期是相符合的!但是并没有结束,动态规划有一个后无效性原则(当前状态只与前一个状态有关)。那么我们就可以对dp数组进行压缩处理,将二维数组转换成一维数组。每一次选择物品对这个数组进行更新就可以啦!那么就可以将状态转移方程压缩成为 dp[j]=max(dp[j],dp[j-w[i]]+v[i]) 。不过我们需要注意的是在压缩过后我们需要逆序刷新数组的值,如果正序刷新的话就不能保存上一个阶段对应获取最大价值的值了。那么我们就可以写出以下程序:


#include <stdio.h>
int max(const int a,const int b)
{
    return a>b ? a:b;
}
int main()
{
    int w[35]={0},v[35]={0},dp[210]={0};
    int n,m;
    scanf("%d %d",&m,&n);
    int i,j;
    for(i=1;i<=n;i++){
        scanf("%d %d",&w[i],&v[i]);
    }
    for(i=1;i<=n;i++){
        for(j=m;j>=0;j--){
            if(j>=w[i])//如果当前背包的容量大于商品的质量
            {
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//判断是否应该拿下
            }
        }
        for(int k=0;k<=m;k++)
        {
            printf("%d ",dp[k]);
        }
        printf("\n");
    }
    printf("%d\n",dp[n][m]);
}

在这里插入图片描述

       可以看出和上面输出的dp表并没有什么区别!

完全背包问题

题目描述:

       设有n种物品,每种物品有一个重量及一个价值,但每种物品的数量都是无限的,有一个背包,最大载重量为m,今从n中物品中选取若干件(同一种物品可以多次选取)使其质量小于等于m,而价值的和为最大。

输入:

        第一行:两个整数,M(背包容量,M<= 200)和N(物品数量,N<=30);
        第2…N+1行:每行两个整数Wi,Ci,表示每个物品的质量与价值。

输出:

       仅一行,一个数,表示最大总价值。

样例:


输入:
10 4
2 1
3 3
4 5
7 9
输出:
12

       与01背包问题不同的是这不是每个物品选择拿与不拿的问题了,而是要选择几个该物品,最终选择价值最大的。那么我们可以在01背包的问题上继续进行思考这个问题,01背包中我们知道了之前的状态,那么我无非就是要判断拿k个物品和不拿时进行比较,如果价值比之前大就拿下。而每个种类的物品最多只能拿取j/w[i]个,再加一重循环不就可以啦!程序的核心代码如下:


for(i=1;i<=n;i++){
    for(j=m;j>=0;j--){
        for(k=0;k<=j/w[i];k++)
        {
            dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);//判断是否应该拿下k个商品
        }
    }
}

       通过代码可以发现通过这种朴素的算法是需要三重循环的,好像对时间复杂度比较高。那么我们也借鉴01背包来对完全背包进行打表!

w[i](质量) v[i](价值) 0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0
2 1 0 0 1 1 2 2 3 3 4 4 5
3 3 0 0 1 3 3 4 6 6 7 9 9
4 5 0 0 1 3 5 5 6 8 10 10 11
7 9 0 0 1 3 5 5 6 9 10 10 12

       根据表中红色标记的数值来看,需要注意的是如果背包的容量不能装下当前物品的质量。那么当前容量所能装下价值最大的物品就等于上一个物品所能保存的最大价值。重点看一下4是怎么来的,这个4并不是从 i-1来的,而是从i来的。通过正序推出该物品的价值。状态转移方程就可以写成是 :dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]) 和01背包的唯一区别是max的第二个参数。01背包是i-1,而完全背包是i,而且是通过正序推理得到的状态转移方程。

       根据状态转移方程应该很快就能写出程序了吧!但是根据dp的后无效性原则,对动态规划状态转移方程进行压缩!压缩过后就是dp[j]=max(dp[j],dp[j-w[i]]+v[i]) ,小伙伴们一看是不是和01背包的状态转移方程一模一样啊!但是但是两个有个重大的区别:01背包使用的是上一条的数据,所以需要逆序避免覆盖之前的值,而完全背包是从当前更新后的数据进行相关的操作的 。通过以上分析我们可以写出如下程序:


#include <stdio.h>
int max(const int a,const int b)
{
    return a>b ? a:b;
}
int main()
{
    int w[35]={0},v[35]={0},dp[210]={0};
    int n,m;
    scanf("%d %d",&m,&n);
    int i,j;
    for(i=1;i<=n;i++){
        scanf("%d %d",&w[i],&v[i]);
    }
    for(i=1;i<=n;i++){
        for(j=0;j<=m;j++){
            if(j>=w[i])//如果当前背包的容量大于商品的质量
            {
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//判断是否应该拿下
            }
        }
        for(int k=0;k<=m;k++)
        {
            printf("%d ",dp[k]);
        }
        printf("\n");
    }
    printf("%d\n",dp[m]);
}

在这里插入图片描述

       通过以上代码的输出可以看出打印的dp表和我们推测的并没有什么区别,程序正确!

多重背包问题

题目描述:

       为了庆祝班级在校运会上取得了全校第一名的好成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能够购买最大价值的奖品,可以补充他们的精力和体力。

输入:

       第一行输入两个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。

       接下来的n行,每行3个数,w,v,s分别表示第i种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中w<=100,v<=1000,s<=10;

输出:

       一行:一个数,表示此次购买能获得的最大价值(注意!不是价格)。

示例:

输入:
5 1000
输出:
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1

       与完全背包不同的是:完全背包每个物品的个数是无限的,而多重背包是每个物品只能拿指定的件数。那么最容易想到的方法就是把相同的物品分开,比如说有n个a物品,就将它分成a1 a2 a3 a4…an然后用01背包的方法去解决。那么我们就可以写出以下核心代码:


for(i=1;i<=n;i++){
    for(j=m;j>=0;j--){
        for(k=0;k<=s[i]&&j>=k*w[i];k++)
        {
            dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);//从01背包的状态转移方程,去增加第i个物品拿k个的循环
        }
    }
}

       通过以上的代码可以看出当s[i]特别大的时候那么就会消耗非常多的时间复杂度,那么肯定是有优化的方法的!那么我们可以通过二进制来对这个同一个物品应该拿取几个进行优化。我们可以通过以下问题进行研究:

有1000个苹果,10个箱子怎么放,不管我想拿多少个苹果,都可以成箱成箱的拿?

       用二进制的思想就是每一个箱子代表二进制对应的位数,那么210大于1024应该是可以满足题目条件的。那么每个箱子放的苹果分别是1,2,4,8,16,32,…488(1000-512)。需要一个苹果拿第一箱,需要两个苹果拿第二项,需要三个苹果拿一二箱。那么对于需要拿1000箱的问题本来要循环1000次,经过优化以后只用循环10次就可以啦!那么我们就可以写出以下程序啦!


for(i=1;i<=n;i++){
    for(j=m;j>=0;j--){
        for(k=0;k<=s[i]&&j>=k*w[i];k<<=1)
        {
        	if((k<<1)>s[i]&&j>=k*w[i])
        	{
        		dp[j]=max(dp[j],dp[j-(s[i]-k)*w[i]]+(s[i]-k)*v[i]);
        	}
            else
            	dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);//从01背包的状态转移方程,去增加第i个物品拿k个的循环
        }
    }
}

动态规划解题思路

       对于动态规划问题我们一般的思路如下:

  • 判断是动态规划的解题思路以后立马定义一个数组,把数组对应的下标、对应的值想清楚。
  • 然后根据题目意思找规律进行打表,找出初始状态以及一些边界条件。
  • 根据打表的数据找出状态转移方程。
  • 最后根据状态转移方程进行编写程序。

到此这篇关于C语言动态规划之背包问题详解的文章就介绍到这了,更多相关C语言 背包内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言动态规划之背包问题详解

本文链接: https://www.lsjlt.com/news/124515.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言动态规划之背包问题详解
    01背包问题        给定n种物品,和一个容量为C的背包,物品i的重量是w[i],其价值为v[i]。问如何选择装入背包的物品,使得装入背...
    99+
    2024-04-02
  • C语言动态规划多种背包问题分析讲解
    目录写在前面01背包问题完全背包问题多重背包问题 I多重背包问题 II为什么可以这样优化呢一 、二进制与十进制二 、动态规划的时间复杂度估算三 、多重背包分组背包问题写在前面 之前讲...
    99+
    2024-04-02
  • C语言动态规划多种背包问题怎么解决
    这篇文章主要介绍了C语言动态规划多种背包问题怎么解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C语言动态规划多种背包问题怎么解决文章都会有所收获,下面我们一起来看看吧。01背包问题C语言数学问题与简单DP0...
    99+
    2023-06-30
  • C++动态规划中关于背包问题讲解
    目录一、分割等和子集-最后一块石头的重量II二、目标和三、一和零四、零钱兑换II五、排列与组合组合总数IV(排列问题)零钱兑换(组合问题) 一、分割等和子集-最后一块石头的重量II ...
    99+
    2023-03-15
    C++动态规划背包问题 C++动态规划 C++背包问题
  • Java实现动态规划背包问题
    目录前言一、原理1.1 最优子结构性质1.2 递归关系二、算法描述2.1 算法描述2.2 图解2.3 构造最优解三、 0 − 1 0-1 0−1 背包问题相关...
    99+
    2024-04-02
  • 【动态规划】背包问题(详细总结,很全)
    【动态规划】 一、 背包问题1. 背包问题总结1)动规四部曲:2) 递推公式总结:3) 遍历顺序总结: 2. 01背包1) 二维dp数组代码实现 2) 一维dp数组代码实现 ...
    99+
    2023-09-09
    动态规划 算法 python leetcode
  • C++动态规划中关于背包问题怎么解决
    本篇内容主要讲解“C++动态规划中关于背包问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++动态规划中关于背包问题怎么解决”吧!一、分割等和子集-最后一块石头的重量II背包问题,难...
    99+
    2023-07-05
  • C语言动态内存规划详解
    目录动态内存规划动态内存函数的介绍总结动态内存规划 用C语言写程序时,因为语言的一些特性使用数组的时候只能用常量来申明数组,就导致数组的内存被卡得很死,不能根据我们的实际需求灵活的使...
    99+
    2024-04-02
  • Python算法题解:动态规划解0-1背包问题
    概述背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给...
    99+
    2023-06-02
  • java背包问题动态规划算法分析
    背包问题 【题目描述】 一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1,W2,…,WnW1,W2,…,Wn,它们的价值分别为C1,C2,…,C...
    99+
    2024-04-02
  • 动态规划之什么是多重背包
    本篇内容主要讲解“动态规划之什么是多重背包”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“动态规划之什么是多重背包”吧!多重背包有N种物品和一个容量为V 的背包。...
    99+
    2024-04-02
  • Java使用动态规划算法思想解决背包问题
    目录动态规划算法动态规划算法的思想最优性原理动态规划算法的三大特点动态规划算法中的0/1背包问题动态规划算法的优点小结动态规划算法 动态规划算法的思想 动态规划算法处理的对象是多阶段...
    99+
    2024-04-02
  • python 动态规划问题解析(背包问题和最长公共子串)
    目录背包问题最长公共子串背包问题 现在要往一个可以装4个单位重量的背包里怎么装价值最高:A重量1个单位,价值15;B重量3个单位,价值20;C重量4个重量,价值30 使用动态规划填充...
    99+
    2024-04-02
  • C语言 深入理解动态规划之计数类DP
    目录写在前面石子合并写在前面 之前讲过背包问题,线性DP,区间DP,不知道大家忘了吗,这次是计数类DP 石子合并 老规矩,先画图。 思路:把1,2,3, … n分别...
    99+
    2024-04-02
  • C语言深入探究动态规划之区间DP
    目录写在前面石子合并写在前面 之前讲过背包问题,线性DP不知道大家忘了吗,这次是区间DP 石子合并 题意: 合并 N 堆石子,每次只能合并相邻的两堆石子,求最小代价 解题思路: ...
    99+
    2024-04-02
  • C语言深入探究动态规划之线性DP
    目录写在前面数字三角形最长上升子序列最长上升子序列 II最长公共子序列写在前面 之前讲过背包问题,不知道大家忘了吗,如果忘了可以点这里,这次是线性DP 数字三角形 状态表示:f[i...
    99+
    2024-04-02
  • C语言数学问题与简单DP01背包问题详解
    目录数学买不到的数目蚂蚁感冒饮料换购简单DP01背包问题二维一维数学 顾名思义,数学类的题就是都可以用数学知识求解。 买不到的数目 这是第四届蓝桥杯省赛C++A组,第四届蓝桥杯省赛J...
    99+
    2024-04-02
  • Java动态规划之丑数问题实例讲解
    问题描述: 我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。 注: 1也是丑数 思路: 我们来分析一下丑数如何得到,肯...
    99+
    2024-04-02
  • c语言动态规划算法是什么
    C语言动态规划算法是一种用于解决优化问题的算法。它通过将问题划分为子问题,并保存子问题的解来避免重复计算,从而提高算法的效率。动态规...
    99+
    2023-08-18
    c语言
  • C++实现动态规划过程详解
    目录C++实现动态规划1. 动态规划的基础2. 动态规划的实现方法3. 实际应用C++实现动态规划 动态规划是解决一类最优问题的常用方法,它是解决最优化问题的一种途径,因为这种算法通...
    99+
    2023-05-20
    C++实现动态规划 C++动态规划
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作