0-1背包

news2024/9/27 17:25:19

问题概述:

0-1背包是在n件物品取出若干件放在空间为V的背包里,每件物品的体积为v[ i ],与之相对应的价值为w[ i ],要求在不超过背包空间的情况下,得到的物品的价值总和最大,问这个最大值是多少?

问题分析:

遇到这类问题,我们有两种思路:

1.对一个物品考虑选或不选(站在物品的角度)

2.枚举选哪个(站在选的角度)

而0-1背包是个经典的选或不选思想的代表,我们首先从回溯的角度来分析该问题,假设我们选第i个物品,那么在选择剩下的i-1个物品时,剩余空间是capacity-v[i],得到的总价值+w[i],如果不选第i个物品,那么空间和价值都不会变化,由上面的分析可得到状态转移方程为

dfs(i,j)=max{dfs(i - 1 , j) , dfs(i - 1 , j - v[ i ] )+w[ i ]}

(这里面的i代表前i个元素,就代表剩余的空间,返回的是所得到的最大价值)

递归边界条件:i<0时,没有物品可供选择,返回0

                         j<v[i]时,即剩余空间不够存放该物品,只能返回前i - 1个物品所得的最大值

递归入口:dfs(n-1,capacity),n-1是最后一个元素的下标

回溯代码如下

int ZeroOne(int i, int capacity, int v[], int w[])//这里的v[]和w[]记录物品的体积和价值
{
    if (i < 0)//代表没有物品可以选择
        return 0;
    if (capacity < v[i])//物品体积大于容器的体积,直接返回前i-1个物品的最大价值
        return ZeroOne(i - 1, capacity, v, w);
    //选择第i个物品,那么在选择前i-1个物品时,体积为capcity-v[i]
    //不选第i个物品,那么在选择前i-1个物品时,体积为capacity不变
    //选取两者的较大值
    return fmax(ZeroOne(i - 1, capacity, v, w), ZeroOne(i, capacity - v[i], v, w) + w[i]);
}

当然,我们也可以用一个二维数组记忆化搜索减少时间复杂度,这里就不写了,留给读者当做练习,不会转化的可以看看之前Leetcode---350周赛的第三题,这题比那题简单,不用状态压缩

翻译成递推:

递推的主要方程就是:f[ i ][ j ]=max{ f[i-1][j],f[i-1][ j - v[i] + w[i] ] } 等价于之前回溯用的方程,数组的初始化看回溯的边界条件,当没有物品时f[ 0 ][ j ]=0,也就是把数组的第一行赋值为0,代码如下

//转化成递推
int Max(int a,int b){
    return a>b?a:b;
}
int ZeroOne(int n, int capacity, int v[], int w[]) {
    int f[n+1][capacity+1];//注意数组的大小
    memset(f,0,sizeof(f));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= capacity; j++) {
            if (j < v[i])
                f[i + 1][j] = f[i][j];
            else
                f[i + 1][j] = Max(f[i][j], f[i][j - v[i]] + w[i]);
        }
    }
    return f[n][0];
}

动态规划思想

刚刚通过回溯思想逆向得到,上面这段代码,那么如果我们直接将这道题当作动态规划,又该如何思考呢?

分为三个步骤:

1.确定数组含义:f[i][j]代表前i个物品,j个空间大小所能获得的最大价值和-----这里f数组的含义得靠做题积累,目前我还没找到什么比较好的方法来直接确定数组含义

2.找到状态转移方程:f[ i ][ j ]=max{ f[i-1][ j ],f[i-1][ j-v[ i ] ]+w[ i ] }   j>=v[i]

                                    f[ i ][ j ]=f[i-1][ j ]                                               j<v[i]

方程的来历:这里我们在确定了数组含义之后,就要根据它来找到所谓的转移方程,很显然,f[i][j]得由前i-1个物品得到的最大值和第i个物品选不选共同决定如果选,f[i][j]=f[i-1][j-v[i]]+w[i]如果不选,f[i][j]=f[i-1][j],题目要求找最大值,所以我们取较大值,当然要考虑到空间小于物品大小的情况

(很多人都有这样一个疑惑,为什么这里f[i][j]只和f[i-1][ ]有关,也有可能和f[i-2][ ]有关啊?这里就有一点要明确:一定要弄懂自己定义的数组含义,f[i-1][j]代表的就是前i-1个物品,j个空间大小所能获得的最大价值和,也就是说前i-2个物品的情况已经被包含在f[i-1][]中了,我们就没有必要考虑i-1之前的状态了)

3.数组初始化:当没有物品选择时,f[0][j]=0,即数组第一行赋值为0

代码如下

int Max(int a,int b){
    return a>b?a:b;
}
int ZeroOne(int n, int capacity, int v[], int w[]) {
    int f[n+1][capacity+1];//注意数组的大小
    memset(f,0,sizeof(f));//这里调用库函数直接将数组全部赋值为0
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= capacity; j++) {
            if (j < v[i])
                f[i + 1][j] = f[i][j];
            else
                f[i + 1][j] = Max(f[i][j], f[i][j - v[i]] + w[i]);
        }
    }
    return f[n][0];
}

启发:其实你会发现和之前回溯转递推的代码一样,但是这其中的思维过程却是大相径庭,一般人哪能想到这个dp数组的含义,但是回溯不同,它更容易想,更符合我们的思维模式,这边建议做动态规划题时可以先用回溯想一想

LeetCode---350周赛的第四题

好,接下来,我们用0-1背包解决一下,没看过的小伙伴可以先去我之前的Leetcode---350周赛解析看看该题的一般思路,再来感受感受0-1背包的写法

根据题意得:付费刷的墙+免费刷的墙>=总墙数,免费刷的墙=付费刷墙所需要的时间

假设付费刷了3堵墙,那么不等式变为

3+time0+time1+time2>=n(time0,time1,time2分别是3堵墙的需要的刷墙时间) 

(time0+1)+(time1+1)+(time2+1)>=n,这不就是共有n个物品,每个物品的体积是time[i]+1,价值是cost[i],要求所选物品的体积和>=n,需要的最小价值和

代码如下

#define MIN(x,y) ((x)>(y)?(y):(x))
//以下的全局变量是为了减少dfs函数的参数,使得逻辑更清晰,当然将它们作为参数也是可以的
int*Cost;
int*Time;
int**memo;
int dfs(int i,int j){
    if(j<=0)return 0;
    if(i<0)return INT_MAX/2;//方案非法,返回一个答案范围之外的较大值,不影响后面的取值
    if(memo[i][j]!=-1)
        return memo[i][j];
    return memo[i][j]=MIN(dfs(i-1,j),dfs(i-1,j-1-Time[i])+Cost[i]);
}
int paintWalls(int* cost, int costSize, int* time, int timeSize){
    int n=costSize;
    Cost=cost;
    Time=time;
    memo=(int**)malloc(sizeof(int*)*n);
    for(int i=0;i<n;i++){
        memo[i]=(int*)malloc(sizeof(int)*(n+1));
        for(int j=0;j<n+1;j++){
            memo[i][j]=-1;
        }
    }
    int res = dfs(n-1,n);
    for(int i=0;i<n;i++)
        free(memo[i]);
    free(memo);
    return res;
}

转换成递推:

#define MIN(x,y) ((x)>(y)?(y):(x))
#define MAX(x,y) ((x)>(y)?(x):(y))
int paintWalls(int* cost, int costSize, int* time, int timeSize){
    int n=costSize;
    int f[n+1][n+1];
    memset(f,0,sizeof(f));
    for(int i=1;i<n+1;i++){
        f[0][i]=INT_MAX/2;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<=n;j++){
            //这里注意要将j-1-time[i]和0作比较,如果它小于0,它的状态和0的状态一样
            f[i+1][j]=MIN(f[i][j],f[i][MAX(0,j-1-time[i])]+cost[i]);
        }
    }
    return f[n][n];
}

//优化空间复杂度
int paintWalls(int* cost, int costSize, int* time, int timeSize){
    int n=costSize;
    int f[n+1];
    f[0]=0;
    for(int i=1;i<n+1;i++)
        f[i]=INT_MAX/2;
    for(int i=0;i<n;i++){
        for(int j=n;j>=1;j--){
            f[j]=MIN(f[j],f[MAX(0,j-1-time[i])]+cost[i]);
        }
    }
    return f[n];
}

最后,不要忘记点赞评论加收藏哦!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/688526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pod 知识点 下

上一篇分享了 pod 的基本知识点&#xff0c;有 K8S 环境的小伙伴还是可以用起来的&#xff0c;还对比较简单&#xff0c;知道了 pod 的 yaml 文件结构&#xff0c;标识&#xff0c;基本的创建 pod 和删除 pod 的用法等等&#xff0c;我们继续 pod 的基本分类 前面我们说到了 p…

什么是Natural Language Generation(NLG)?

文章目录 1.NLG的定义2.NLP的步骤3.NLG生成文本方式有哪些&#xff1f;3.1.简单的数据合并3.2.模板化的NLG3.3.高级NLG 4.NLG的应用有哪些&#xff1f; 1.NLG的定义 自然语言生成&#xff08;Natural Language Generation, NLG&#xff09;是NLP&#xff08;自然语言处理&…

Sikulix自动化工具的使用

1.Sikuli-x简介 Sikuli是识别和控制GUI组件进行UI自动化测试的技术&#xff0c;它是有MIT的研究人员开发进行设计的。Sikuli在墨西哥维乔印第安人(Huichol Indians)的语言里是上帝之眼的意思&#xff0c;Sikuli的工作模式与人眼一样&#xff0c;直接识别图像。 Sikuli-x是Sik…

1、Redis入门与安装配置

是什么&#xff1f; Remote Dictionary Server(远程字典服务) 是完全开源的&#xff0c;使用ANSIC语言编写遵守BSD协议&#xff0c;是一个高性能的Key-Value数据库。提供了丰富的数据结构&#xff0c;例如String、Hash、List、Set、SortedSet等等。数据是存在内存中的&#xf…

python爬虫-逆向实例小记-2

注意&#xff01;&#xff01;&#xff01;&#xff01;某数据网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01;&#xff01; 案例分析 第一步&#xff1a;分析请求和响应内容。该网站任何一请求和内容都不可直接…

网站SEO优化的注意事项

SEO作为一种网络营销方式&#xff0c;高投入产出比是其优势所在。通过SEO优化为站点带来大量的主动搜索自然流量&#xff0c;对于企业主来说&#xff0c;是非常必要的。搜索引擎算法在不断发展&#xff0c;SEO技术也在不断发展。那么&#xff0c;如何能做好SEO呢&#xff1f;小…

投出去的简历无人问津,原因竟然在这……

近期收到了不少朋友的反馈说&#xff0c;失业半个月无人问津&#xff0c;放在前些年第二天开始面试有人找&#xff0c;或者是第二天入职了……&#xff0c;为啥明明不招人要挂着招人的岗位&#xff1f; 随着移动互联网的发展&#xff0c;Android市场的需求也在不断变化和升级。…

【springboot】—— 后端Springboot项目开发

后端Springboot项目开发 步骤1 先创建数据库&#xff0c;并在下面创建一个user表&#xff0c;插入数据&#xff0c;sql如下&#xff1a; CREATE TABLE user (id int(11) NOT NULL AUTO_INCREMENT COMMENT ID,email varchar(255) NOT NULL COMMENT 邮箱,password varchar(255)…

Turf.js:用于地理空间分析的 JavaScript 库

https://turfjs.org/ 处理和分析地理空间数据在许多应用程序和平台中发挥着至关重要的作用。如地图绘制、路径规划、基于位置的服务和地理空间分析。Turf.js 是一个专门为执行地理空间操作而开发的开源 JavaScript 库。在本文中&#xff0c;我将详细探讨 Turf.js &#xff0c;…

Linux系统下列出库文件中的符号指令(nm)

文章目录 1 nm指令2 符号类型的含义3 简单示例 1 nm指令 nm是names的缩写&#xff0c; nm命令主要是用来列出某些文件中的符号&#xff08;说白了就是一些函数和全局变量等&#xff09;。 nm命令的输出包含三个部分&#xff1a; 1 符号值。默认显示十六进制&#xff0c;也可以…

SNMP 计算机网络管理 实验3(二)SNMP协议工作原理验证与分析

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

从零手写微前端qiankun框架【超详细万字长文】

项目创建 我们创建如图几个文件夹 main&#xff1a;主应用&#xff08;采用vue3作为技术栈&#xff09;react&#xff1a;子应用1vue2&#xff1a;子应用2vue3&#xff1a;子应用3service&#xff1a;服务端代码 vue2子应用&#xff1a; 我们在App.vue中写一点点东西 <t…

Java微服务金融项目智牛股-基础知识一(CAT链路监控)

CAT链路监控 背景&#xff1a;从单体架构到微服务架构的演变&#xff0c; 一个业务请求往往会流转多个服务&#xff0c; 大型互联网产品服务架构尤为复杂&#xff0c;腾讯的抢红包服务&#xff0c; 阿里的交易支付服务&#xff0c; 可能就流转成百上千个服务节点&#xff0c; 面…

HashMap夺命14问

1. HashMap的底层数据结构是什么&#xff1f; 在JDK1.7中和JDK1.8中有所区别&#xff1a; 在JDK1.7中&#xff0c;由”数组链表“组成&#xff0c;数组是HashMap的主体&#xff0c;链表则是主要为了解决哈希冲突而存在的。 在JDK1.8中&#xff0c;有“数组链表红黑树”组成。当…

【软考网络管理员】2023年软考网管初级常见知识考点(19)-防火墙与入侵检测系统IDS

涉及知识点 防火墙有哪些及其功能&#xff0c;防火墙的区域划分及工作模式&#xff0c;IDS是什么及其作用&#xff1f;入侵检测系统的分类及原理&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;…

java.sql.Time 字段时区问题 Jackson 源码分析 意想不到的Time处理类

java.sql.Time 字段时区问题 系列文章目录 第一章 初步分析 第二章 Mybatis 源码分析 第三章 Jackson 源码分析 意想不到的Time处理类 文章目录 java.sql.Time 字段时区问题 系列文章目录前言Jackson 源码阅读1. 先找 JsonFormat.class 打断点一步步跟踪2. 跟踪进入实际处理类…

RTSP视频流相关的一些操作

播放rtsp camera 内容 端口554在网络通信中用于Real Time Streaming Protocol(RTSP)。 gst-launch-1.0 playbin urirtsp://admin:WANGfengtu1210.0.20.190:554/client0x gst-launch-1.0 playbin urirtsp://admin:WANGfengtu1210.0.20.61:554/client1xgst-launch-1.0 rtspsrc …

基于Arduino UNO的循迹小车

目录 1.analogWrite函数的使用 2.红外循迹模块介绍 3.循迹小车代码实现 4.实物示例 1.analogWrite函数的使用 用analogWrite来替换digitalWrite 说明 将一个模拟数值写进Arduino引脚。这个操作可以用来控制LED的亮度, 或者控制电机的转速. 在Arduino UNO控制器中&#…

关于二叉树的操作,详细操作与实现方法

树是数据结构中的重中之重&#xff0c;尤其以各类二叉树为学习的难点。在面试环节中&#xff0c;二叉树也是必考的模块。本文主要讲二叉树操作的相关知识&#xff0c;梳理面试常考的内容。一起来复习吧。 本篇针对面试中常见的二叉树操作作个总结&#xff1a; 前序遍历&#x…

Kubernetes(k8s)容器编排控制器使用

目录 1 Pod控制器1.1 Pod控制器是什么1.2 Pod和Pod控制器1.3 控制器的必要性1.4 常见的控制器1.4.1 ReplicaSet1.4.2 Deployment1.4.3 DaemonSet 2 ReplicaSet控制器2.1 ReplicaSet概述2.2 ReplicaSet功能2.2.1 精确反应期望值2.2.2 保证高可用2.2.3 弹性伸缩 2.3 创建ReplicaS…