【五】【C语言\动态规划】删除并获得点数、粉刷房子、买卖股票的最佳时机含冷冻期,三道题目深度解析

news2025/1/19 8:10:52

动态规划

动态规划就像是解决问题的一种策略,它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题,并将每个小问题的解保存起来。这样,当我们需要解决原始问题的时候,我们就可以直接利用已经计算好的小问题的解,而不需要重复计算。

动态规划与数学归纳法思想上十分相似。

数学归纳法:

  1. 基础步骤(base case):首先证明命题在最小的基础情况下成立。通常这是一个较简单的情况,可以直接验证命题是否成立。

  2. 归纳步骤(inductive step):假设命题在某个情况下成立,然后证明在下一个情况下也成立。这个证明可以通过推理推断出结论或使用一些已知的规律来得到。

通过反复迭代归纳步骤,我们可以推导出命题在所有情况下成立的结论。

动态规划:

  1. 状态表示:

  2. 状态转移方程:

  3. 初始化:

  4. 填表顺序:

  5. 返回值:

数学归纳法的基础步骤相当于动态规划中初始化步骤。

数学归纳法的归纳步骤相当于动态规划中推导状态转移方程。

动态规划的思想和数学归纳法思想类似。

在动态规划中,首先得到状态在最小的基础情况下的值,然后通过状态转移方程,得到下一个状态的值,反复迭代,最终得到我们期望的状态下的值。

接下来我们通过三道例题,深入理解动态规划思想,以及实现动态规划的具体步骤。、

740. 删除并获得点数

题目解析

我们发现选择了一个数字,假设是x,那么x+1和x-1我们都不能再选择,这种模式非常像打家劫舍题目的模式。由此我们可以把此问题转化为打家劫舍问题。

由于1<=nums[i]<=1e4,所以我们可以创建一个1e4+1大小的数组count,count[i]表示i数字拥有的点数,也就是i*(i出现的次数),这样我们就可以把原问题转化为打家劫舍问题。

状态表示

我们将nums数组转化为count数组,接着对count数组分析即可。

我们可以定义dp[i]表示,1到i 这些数中,我们选择其中不相邻的部分项,所能得到的最大点数。

状态转移方程

我们想一想dp[i]怎么由其他的状态推导出来。

dp[i]表示,1到i 这些数中,我们选择其中不相邻的部分项,所能得到的最大点数。

dp[i-1]表示,1到i-1 这些数中,我们选择其中不相邻的部分项,所能得到的最大点数。

dp[i-2]表示,1到i-2 这些数中,我们选择其中不相邻的部分项,所能得到的最大点数。

我们对i的状态进行研究,一共可以分为两种情况,要么1-i这些数中,我们选择其中不相邻的部分项,其中选择了i,要么我们不选择i。

当我们选择i,那我们就不可能选择i-1,所以此时最大的点数应该是dp[i-2]+count[i]

如果我们不选择i,那我们就可以选择i-1,此时最大的点数应该是dp[i-1],也就是从1到i-1这些数选择不相邻的部分项所能获得的最大点数。

故状态转移方程为,dp[i]=max(dp[i-2]+count[i],dp[i-1])

初始化

由状态转移方程我们知道,想要推导出i位置的状态,需要用到i-1和i-2位置的状态,所以我们需要初始化前两个位置的状态,也就是下标1和2。因为count[0]是不考虑的,nums>=1。

很容易可以得出。

dp[0]=count[0]

dp[1]=max(count[0],count[1])

填表顺序

从左到右

返回值

返回最后一个元素即dp[1e4]

代码实现

 
int deleteAndEarn(int* nums, int numsSize) {
    int n=numsSize;
    const int m=1e4+1;
    int count[m];
    memset(count,0,sizeof(count));
    for(int i=0;i<n;i++){
        count[nums[i]]+=nums[i];
    }

    int dp[m];
    dp[1]=count[1];
    dp[2]=fmax(count[1],count[2]);

    for(int i=3;i<=1e4;i++){
        dp[i]=fmax(dp[i-2]+count[i],dp[i-1]);
    }
    return dp[m-1];
}

我们知道nums中存储的数字的范围是1到1e4,所以我们可以利用count数组, 定义count[i]表示i数字拥有的点数,我们只需要对count数组做一次打家劫舍就可以了。

LCR 091. 粉刷房子

题目解析

状态表示

如果我们定义dp[i]表示从0号房子到i号房子,我们选择不同的粉刷方案,所花费的最小金额数,我们想一想此状态表示的状态转移方程怎么推导?我们具体分析i号房子的状态,一共有三种情况,要么i号房子粉刷成红色,要么i号房子粉刷成绿色,要么i号房子粉刷成蓝色。对于这三中国情况,我们没办法只通过前面几个房子的最小花费数而得到i号房子的最小花费数,因为当我确定i号房子的粉刷颜色,前面的房子的粉刷颜色我没办法确定。所以我们的状态表示应该还需要包括粉刷的颜色。

我可以对其改进,定义dp[i][j]表示从0号房子开始到i号房子,选择不同的粉刷方案,且i号房子粉刷j颜色时的最小花费。其中j的取值是0、1或者2,分别表示红色绿色蓝色。

这样我们就是推导出状态转移方程,一步一步推导状态。

状态转移方程

我们想一想dp[i][j]的状态值如何通过其他的状态推导出来。

dp[i][j]表示从0号房子开始到i号房子,选择不同的粉刷方案,且i号房子粉刷j颜色时的最小花费。

dp[i-1][j]表示从0号房子开始到i-1号房子,选择不同的粉刷方案,且i号房子粉刷j颜色时的最小花费。

对于i号房子,我们依旧分为三种情况,要么粉刷红色要么粉刷绿色要么粉刷蓝色。

当我们对i号房子粉刷红色时,很明显,dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i][0]

i-1号房子粉刷的颜色不能是红色,要么是绿色要么是蓝色,从0号房子到i-1号房子,且i-1号房子粉刷成绿色蓝色时最小的花费分别是dp[i-1][1],dp[i-1][2]

选择较小的一个然后加上i号房子粉刷红色的花费就是dp[i][0]

同理dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i][1]

dp[i][2]=min(dp[i-1][0],dp[i-1][1])+costs[i][2]

故,状态转移方程为

dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i][0]

dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i][1]

dp[i][2]=min(dp[i-1][0],dp[i-1][1])+costs[i][2]

初始化

根据状态转移方程,我们知道推导出i房子粉刷红绿蓝三个状态需要i-1号房子的三个状态,所以我们需要初始化第一个房子的三个状态值。

即,

dp[0][0]=costs[0][0]; dp[0][1]=costs[0][1]; dp[0][2]=costs[0][2];

填表顺序

从左往右填写,一次填写三个状态

返回值

返回最后一个房子三个状态中最小的那个花费。

即,return fmin(dp[n-1][0],fmin(dp[n-1][1],dp[n-1][2]));

代码实现

 

int minCost(int** costs, int costsSize, int* costsColSize){
    int n=costsSize;
    int dp[n][3];

    dp[0][0]=costs[0][0];
    dp[0][1]=costs[0][1];
    dp[0][2]=costs[0][2];

    for(int i=1;i<n;i++){
        dp[i][0]=fmin(dp[i-1][1],dp[i-1][2])+costs[i][0];
        dp[i][1]=fmin(dp[i-1][0],dp[i-1][2])+costs[i][1];
        dp[i][2]=fmin(dp[i-1][0],dp[i-1][1])+costs[i][2];
    }

    return fmin(dp[n-1][0],fmin(dp[n-1][1],dp[n-1][2]));
}

309. 买卖股票的最佳时机含冷冻期

题目解析

状态表示

如果我们定义dp[i]表示从第0天开始到第i天,我们所得到利润的最大值,我们没办法通过其他的状态值推导出i位置的状态值。仔细想想,其实第i天的状态还可以细分。

可以分为第i天结束时,手上有股票,或者手上没股票且在冷冻期,或者手上没股票且不在冷冻期。

所以我们可以定义 dp[i][j]表示从第0天到第i天,所获得的最大利润值,且第i天结束时拥有状态j(j=0表示手上有股票,j=1表示手上没股票,且在冷冻期,j=2表示手上没股票,且不在冷冻期)。

状态转移方程

我们想一想第i天的三个状态能不能由其他的状态推导得出。

dp[i][j]表示从第0天到第i天,所获得的最大利润值,且第i天结束时拥有状态j

dp[i-1][j]表示从第0天到第i-1天,所获得的最大利润值,且第i天结束时拥有状态j

(j=0表示手上有股票,j=1表示手上没股票,且在冷冻期,j=2表示手上没股票,且不在冷冻期)。

当第i天结束的时候,我们手上有股票,对应dp[i][0],说明第i天白天我们买了股票,或者第i-1天我们手上有股票,且第i天白天我们没有卖掉。

也就是第i-1天结束时,要么是手上有股票,要么是手上没股票且不在冷冻期。

所以dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i])

第i天买了股票,所以利润需要减去股票价值。

当第i天结束的时候,我们手上没有股票,且在冷冻期,对应dp[i][1],说明第i天白天我们卖了股票,所以在第i天结束的时候处于冷冻期。所以要么第i-1天手上有股票,然后第i天白天我们卖掉了,要么第i-1天我们手上没有股票且不在冷冻期,第i天白天我们买了股票又卖了股票。

所以dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][2])

当第i天结束的时候,我们手上没有股票,且不在冷冻期,对应 dp[i][2],说明第i天白天我们没有卖股票,要么i-1天白天我们卖了股票,要么i-1天白天也没卖股票,然后第i天白天什么都没做。也就是i-1天结束时的状态要么是手上没股票,且在冷冻期,要么是手上没股票,且不在冷冻期。(i-1天结束时的状态要么是手上没股票,且在冷冻期,然后冷冻期一直持续到第i天结束前,结束时冷冻期结束。)

所以dp[i][2]=max(dp[i-1][1],dp[i-1][2])

故状态转移方程为,

dp[i][0]=fmax(dp[i-1][0],dp[i-1][2]-prices[i]); dp[i][1]=fmax(dp[i-1][0]+prices[i],dp[i-1][2]); dp[i][2]=fmax(dp[i-1][1],dp[i-1][2]);

初始化

通过状态转移方程,我们知道推导出i天的状态需要i-1天的状态,所以我们需要初始化第0天的三个状态。

dp[0][0]表示第0天结束时手上有股票,说明白天买了股票,利润为负的prices[0]。

dp[0][1]表示第0天结束时手上没有股票,且在冷冻期,不可能存在的状态,所以该状态的值不能影响后面的状态,利润置0就可以了。因为会取到这个值的状态转移方程是dp[i][2]=fmax(dp[i-1][1],dp[i-1][2]);利润置0,dp[i-1][2]不为零,就不会取到dp[i-1][1],dp[i-1][2]为零,dp[i-1][1]也不会影响dp[i]的推导。

dp[0][2] 表示第0天结束时手上没有股票,且不在冷冻期,说明白天没有卖股票,dp[0][2]=0;

所以初始化为,

dp[0][0]=-prices[0]; dp[0][1]=0; dp[0][2]=0;

填表顺序

从左往右填写,一次性填写三个状态

返回值

返回最后一天中,三个状态的最大值,即

return fmax(dp[n-1][0],fmax(dp[n-1][1],dp[n-1][2]));

代码实现

 
int maxProfit(int* prices, int pricesSize) {
    int n=pricesSize;
    int dp[n][3];//0:有票   1:无票且在冷冻期   2:无票且不在冷冻期

    dp[0][0]=-prices[0];
    dp[0][1]=0;
    dp[0][2]=0;

    for(int i=1;i<n;i++){
        dp[i][0]=fmax(dp[i-1][0],dp[i-1][2]-prices[i]);
        dp[i][1]=fmax(dp[i-1][0]+prices[i],dp[i-1][2]);
        dp[i][2]=fmax(dp[i-1][1],dp[i-1][2]);
    }

    return fmax(dp[n-1][0],fmax(dp[n-1][1],dp[n-1][2]));

}

结尾

今天我们学习了动态规划的思想,动态规划思想和数学归纳法思想有一些类似,动态规划在模拟数学归纳法的过程,已知一个最简单的基础解,通过得到前项与后项的推导关系,由这个最简单的基础解,我们可以一步一步推导出我们希望得到的那个解,把我们得到的解依次存放在dp数组中,dp数组中对应的状态,就像是数列里面的每一项。最后感谢您阅读我的文章,对于动态规划系列,我会一直更新,如果您觉得内容有帮助,可以点赞加关注,以快速阅读最新文章。

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

Linux磁盘与文件管理

目录 一、磁盘介绍 1. 磁盘数据结构 2. 磁盘的接口类型 3. 磁盘在Linux上的表现形式 二、磁盘分区与MBR 1. 分区优缺点 2. 分区方式 3. MBR分区 4. GPT分区 三、文件系统 1. 文件系统的组成 2. 默认的文件系统 3. 文件系统的作用 4. 模拟破坏文件与修复文件 4…

软件测试/测试开发丨Python学习笔记之内置库科学计算、日期与时间处理

Python 内置库 - 科学计算 了解 math 函数 math 函数&#xff0c;python 提供的内置数学类函数库&#xff0c;包含了很多数学公式。 比如幂函数运算&#xff0c;三角函数&#xff0c;高等函数运算等。 math 函数操作 数字常数数论与表示函数幂对数函数三角对数函数高等特殊…

vue3-12

需求是用户如果登录了&#xff0c;可以访问主页&#xff0c;如果没有登录&#xff0c;则不能访问主页&#xff0c;随后跳转到登录界面&#xff0c;让用户登录 实现思路&#xff0c;在用户登录之前做一个检查&#xff0c;如果登录了&#xff0c;则token是存在的&#xff0c;则放…

FreeRTOS 实时操作系统第八讲 - 时间管理 (系统节拍,延时函数)

一、系统节拍 FreeRTOS 实时操作系统需要一个时钟节拍&#xff0c;以供系统处理诸如延时、超时、软件定时器等与时间相关的事件。 时钟节拍是周期性定时中断&#xff0c;这个中断可以看做是系统心跳。中断时间间隔取决于不同的应用&#xff0c;一般是 1ms – 100ms。时钟的节拍…

泰迪智能科技“供需对接就业育人项目”介绍

为帮助用人单位培养和招聘更多实用型、复合型和紧缺型人才,推动高校人才培养与就业有机联动、人才供需有效对接促进高校毕业生更加充分更高质量就业&#xff0c;经广东泰迪智能科技股份有限公司申报、全国高校毕业生就业创业指导委员会专家组审核&#xff0c;泰迪智能科技“供需…

flutter 安卓使用高德插件黑屏

地址 https://lbs.amap.com/api/android-sdk/guide/create-project/android-studio-create-project 下面介绍的方式是Native配置 sdk&#xff0c;也就是需要手动下载到本地在引入的方式 1、添加 jar 文件&#xff1a; 将下载的地图 SDK 的 jar包复制到工程&#xff08;此处截…

【回溯】最大团问题Python实现

文章目录 [toc]问题描述回溯法时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;回溯法 问题描述 给定无向图 G ( V , E ) G (V , E) G(V,E)&#xff0c;如果 U ⊆ V U \subseteq V U⊆V&#xff0c;且对任意 u u u&#xff0c; v ∈ U v \in U v∈U有…

fork函数详解【Linux】

fork函数详解【Linux】 fork函数的概念fork调用后的底层细节解释fork学习中的一些笔记和问题fork的写实拷贝深拷贝的策略 fork调用失败的原因 fork函数的概念 调用fork函数可以在已存在的进程中创建一个子进程&#xff0c;此时&#xff0c;新进程叫做子进程&#xff0c;原进程叫…

100000000!文心一言披露最新用户规模

“文心一言用户规模突破1亿。” 12月28日&#xff0c;百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰在第十届WAVE SUMMIT深度学习开发者大会上宣布。会上&#xff0c;王海峰以《文心加飞桨&#xff0c;翩然赴星河》为题作了主旨演讲&#xff0c;分享了飞桨和文…

【tcp】TCP CLOSE_WAIT问题分析与定位

一、问题背景 某日&#xff0c;运维突然在群里突然丢出告警信息&#xff1a; 对象类型&#xff1a;主机 检测规则&#xff1a;NET.TCP.CLOSE.WAIT 告警内容&#xff1a;CLOSE_WAIT状态的TCP连接数大于500 ....image.png 上面告警信息已经说的很明白&#xff0c;CLOSE_WAIT状…

【UE在关卡序列制作图片序列,捕获影片时出现小白人下落的场景或者空场景】

UE在关卡序列制作图片序列,捕获影片时出现小白人下落的场景或者空场景 问题 下面是有问题的截图 1.如果场景没有放角色就是纯天空 2.如果有角色就是角色一直在下落 原因 使用影片场景捕获&#xff08;旧版时&#xff09;&#xff0c;如果镜头帧率的长度没有和影片的长度一致…

操作教程|MeterSphere UI测试+VNC:简单、快捷地查看UI测试实时执行详情

编者注&#xff1a;本文为CSDN博主hxe116的原创文章。 原文链接为&#xff1a;https://blog.csdn.net/hxe116/article/details/134714960?spm1001.2014.3001.5502 作为一款一站式的开源持续测试平台&#xff0c;MeterSphere涵盖了测试跟踪、接口测试、UI测试和性能测试等功能…

YOLO格式转VOC格式

#仅支持图片格式统一的,多格式图片需要完善 from xml.dom.minidom import Document import os import cv2 from PIL import Image import numpy as np def makexml

【MAX30102 T03】心率血氧传感器

目录 一、实物图 二、原理图 引脚定义 三、简介 选择模式 I2C 通讯格式&#xff1a; UART 通讯格式&#xff1a; 四、结构尺寸 五、注意&#xff1a; 作者&#xff1a;特纳斯电子 请以底部官方认证的推广方式联系作者 一、实物图 二、原理图 引脚定义 三、简介 血氧心率测量模…

Matplotlib的详细使用及原理

认识matplotlib Matplotlib是一个Python 2D绘图库&#xff0c;能够以多种硬拷贝格式和跨平台的交互式环境生成出版物质量的图形&#xff0c;用来绘制各种静态&#xff0c;动态&#xff0c;交互式的图表。 Matplotlib已经成为python中公认的数据可视化工具&#xff0c;我们所熟…

巨匠纺・品鉴窗帘是一线品牌吗,产品质量怎么样

巨匠纺・品鉴窗帘是一线品牌&#xff0c;产品品质有保障&#xff0c;作为深耕行业多年的窗帘品牌&#xff0c;巨匠纺・品鉴窗帘凭借不断升级的匠心品质、过硬的综合实力和品牌影响力在众多窗帘中脱颖而出&#xff0c;公司先后荣获中国 3.15 诚信品牌、中国绿色环保品牌、中国窗…

【学习笔记】GAN生成对抗神经网络原理与实践

最早在2014年Ian J. Goodfellow等人提出的GAN。 文献为&#xff1a;Generative Adversarial Nets GAN面临的主要挑战有模型训练困难&#xff0c;容易出现生成模型坍塌等问题。因为GAN是采用生成对抗策略来训练的&#xff0c;优化生成模型必然导致判别模型的损失增大。 定义 生…

旧衣回收小程序搭建,稳占回收市场

近几年我国大众的消费水平不断提升&#xff0c;闲置物品也相应增加了不少&#xff0c;尤其是闲置衣服&#xff0c;为了减少资源浪费&#xff0c;旧衣服回收回收行业受到了大众的关注。 目前我国旧衣服回收行业的市场规模达到了300多亿元&#xff0c;旧衣回收行业的商业价值非常…

机器学习之人工神经网络(Artificial Neural Networks,ANN)

人工神经网络(Artificial Neural Networks,ANN)是机器学习中的一种模型,灵感来源于人脑的神经网络结构。它由神经元(或称为节点)构成的层级结构组成,每个神经元接收输入并生成输出,这些输入和输出通过权重进行连接。 人工神经网络(ANN)是一种模仿生物神经系统构建的…

算法基础之整数划分

整数划分 核心思想&#xff1a; 计数类dp 背包做法 f[i][j] 表示 取 1 – i 的物品 总容量为j的选法数量 f[i][j] f[i-1][j] f[i-1][j-v[i]] f[i-1][j-2v[i]] f[i-1][j-3v[i]] ……f[i-1][j-kv[i]] f[i][j-v[i]] f[i-1][j-v[i]] f[i-1][j-2v[i]] f[i-1][j-3v[i]] ……f[i…