每天刷两道题——第四天

本文介绍了如何使用动态规划算法解决最大子数组和问题,并扩展到求解任意子数组和的绝对值最大问题。通过定义子问题、状态转移方程和初始化过程,展示了如何计算给定数组中特定类型的子数组和的最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1最大子数组和

给一个整数数组 nums ,找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

动态规划

   动态规划也称为动态优化,把复杂问题分解为子问题,通过求解子问题,组合子问题的解从而得到整个问题的解。参考文章

动态规划适用于:
1.一个复杂问题的最优解由数个小问题的最优解构成,可以通过寻找子问题的最优解来得到复杂问题的最优解,这个特性又称为最优子结构(optimal substructure);例如:最短路径
2.子问题在复杂问题内重复出现,使得子问题的解可以被存储起来重复利用,这个特性又称为重叠子问题(overlapping subproblem)。例如:斐波拉契数列。

动态规划的无后效性

为了保证计算子问题能够按照顺序、不重复地进行,动态规划要求已经求解的子问题不受后续阶段的影响。这个条件也被叫做「无后效性」。换言之,动态规划对状态空间的遍历构成一张有向无环图,遍历就是该有向无环图的一个拓扑序。有向无环图中的节点对应问题中的「状态」,图中的边则对应状态之间的「转移」,转移的选取就是动态规划中的「决策」。

动态规划题解的步骤:状态定义、状态转移方程、初始化、输出、是否可以空间优化。

1.定义子问题:

子问题 1:以 −2 结尾的连续子数组的最大和是多少;
子问题 2:以 1 结尾的连续子数组的最大和是多少;
子问题 3:以 −3 结尾的连续子数组的最大和是多少;
子问题 4:以 4结尾的连续子数组的最大和是多少;
子问题 5:以 −1 结尾的连续子数组的最大和是多少;
子问题 6:以 2 结尾的连续子数组的最大和是多少;
子问题 7:以 1结尾的连续子数组的最大和是多少;
子问题 8:以 −5结尾的连续子数组的最大和是多少;
子问题 9:以 4 结尾的连续子数组的最大和是多少。

2.定义状态

d p [ i ] dp[i] dp[i]:表示以 n u m s [ i ] nums[i] nums[i]结尾 的 连续 子数组的最大和。

3.状态转移方程(子问题之间的联系)

n u m s [ i ] nums[i] nums[i]结尾的连续子数组与以 n u m s [ i − 1 ] nums[i - 1] nums[i1] 结尾的连续子数组只相差一个元素 n u m s [ i ] nums[i] nums[i]

假设数组 n u m s nums nums 的值全都严格大于 0,那么一定有 d p [ i ] = d p [ i − 1 ] + n u m s [ i ] dp[i] = dp[i - 1] + nums[i] dp[i]=dp[i1]+nums[i]

可是 d p [ i − 1 ] dp[i - 1] dp[i1]有可能是负数,于是分类讨论:
  如果 d p [ i − 1 ] > 0 dp[i - 1] > 0 dp[i1]>0,那么可以把 n u m s [ i ] nums[i] nums[i] 直接接在 d p [ i − 1 ] dp[i - 1] dp[i1] 表示的那个数组的后面,得到和更大的连续子数组;
  如果 d p [ i − 1 ] < = 0 dp[i - 1] <= 0 dp[i1]<=0,那么 n u m s [ i ] nums[i] nums[i] 加上前面的数 d p [ i − 1 ] dp[i - 1] dp[i1] 以后值不会变大。于是 d p [ i ] dp[i] dp[i] 「另起炉灶」,此时单独的一个 n u m s [ i ] nums[i] nums[i] 的值,就是 d p [ i ] dp[i] dp[i]

以上两种情况的最大值就是 d p [ i ] dp[i] dp[i]的值,写出如下状态转移方程:
d p [ i ] = { d p [ i − 1 ] + n u m s [ i ] if  d p [ i − 1 ] > 0 n u m s [ i ] if  d p [ i − 1 ] < = 0 dp[i]= \begin{cases} dp[i-1]+nums[i] &\text{if } dp[i-1]>0\\ nums[i] &\text{if } dp[i-1]<=0 \end{cases} dp[i]={dp[i1]+nums[i]nums[i]if dp[i1]>0if dp[i1]<=0

4.初始化

d p [ 0 ] dp[0] dp[0] 根据定义,只有 1 个数,一定以 n u m s [ 0 ] nums[0] nums[0]结尾,因此 d p [ 0 ] = n u m s [ 0 ] dp[0] = nums[0] dp[0]=nums[0]

5.输出

这个问题的输出是把所有的 d p [ 0 ] 、 d p [ 1 ] 、 … … 、 d p [ n − 1 ] dp[0]、dp[1]、……、dp[n - 1] dp[0]dp[1]……dp[n1]都看一遍,取最大值。

6.是否可以优化空间

一般的问题只要时间复杂度最优就可以。
空间复杂度 o n l i n e j u d g e online judge onlinejudge并不在意,只要使用的空间不太离谱,不要一上来就 i n t [ ] d p = n e w i n t [ I n t e g e r . M A X V A L U E ] int[] dp = new int[Integer.MAX_VALUE] int[]dp=newint[Integer.MAXVALUE]就好。

7.代码

class question1:
	    def maxSubArray(self,nums):
        size=len(nums)
        pre=0  #初始状态
        res=nums[0]  #存储每个状态中的最大值
        for i in range(size):
            pre=max(nums[i],pre+nums[i]) #计算以nums[i]结尾的最大值
            res=max(res,pre)
        return res
q = question1()
print(q.maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))       

1.2任意子数组和的绝对值的最大值

数组 [ n 1 , n 2 , n 3 , . . . , n r − 1 , n r ] 的和的绝对值为 a b s ( n 1 + n 2 + n 3 + . . . + n r − 1 + n r ) 数组[n_1,n_2,n_3,...,n_{r-1},n_r]的 和的绝对值 为abs(n_1+n_2+n_3+...+n_{r-1}+n_r) 数组[n1,n2,n3,...,nr1,nr]的和的绝对值为abs(n1+n2+n3+...+nr1+nr)

其中abs()为取绝对值,定义如下:
1.如果 x 是负整数,那么 a b s ( x ) = − x abs(x) = -x abs(x)=x
2.如果 x 是非负整数,那么 a b s ( x ) = x abs(x) = x abs(x)=x

请你找出 nums 中 和的绝对值 最大的任意子数组(可能为空),并返回该最大值 。子数组是数组中的一个连续非空序列。

输入:nums = [2,-5,1,-4,3,-2]
输出:8
解释:子数组 [-5,1,-4] 和的绝对值最大,为 abs(-5+1-4) = abs(-8) = 8 。

nums 中 和的绝对值 最大的任意子数组可能是这个数组的子数组最大和的绝对值,也可能是这个数组的子数组最小和的绝对值,按照1.1的动态规划思路,求取数组的子数组最大和和子数组最小和。

class question1:
    def maxAbsoluteSum(self,nums):
        preMax,preMin=0,0
        resMax,resMin=nums[0],nums[0]

        for i in range(len(nums)):
            preMax=max(nums[i],nums[i]+preMax)
            resMax=max(resMax,preMax)

            preMin = min(nums[i], nums[i] + preMin)
            resMin = min(preMin, resMin)

        return max(abs(resMax),abs(-resMin))

q = question1()
print(q.maxAbsoluteSum([2,-5,1,-4,3,-2]))
<think>嗯,用户问的是如何十天速成计算机二级C语言。首先,我得考虑用户的需求和背景。计算机二级考试通常包括基础知识和编程,用户可能时间紧迫,需要高效的方法。十天时间确实紧张,但可能用户已经有一定基础,或者只是需要快速复习。 首先,我需要明确考试大纲和重点。计算机二级C语言通常涵盖数据类型、运算符、控制结构、数组、函数、指针、结构体、文件操作等。用户需要知道哪些是高频考点,以便合理分配时间。 接下来,考虑时间分配。十天的话,前三天可能需要过一遍基础知识点,中间四天练习编程和真,最后三天模拟考试和查漏补缺。但得具体规划每一天的任务,比如第一天变量、运算符,第二天控制结构,第三天函数和数组,第四天指针和结构体,第五天文件操作,这样可能更合理。 然后,学习资源方面,用户可能需要推荐一些速成教材或在线资料。比如谭浩强的C语言教材,或者在线库、模拟软件。此外,是关键,尤其是历年真,因为考试型重复率较高。 编程练习部分,用户需要实际编写代码,理解常见算法如排序、查找、递归等。调试能力也很重要,所以得强调多写代码,多调试,熟悉编译器的错误提示。 模拟考试环境也很关键,用户需要适应考试的时间限制和操作流程,特别是上机考试,熟悉编程环境如Visual C++ 6.0或Dev-C++。 可能的误区是用户可能忽略基础,直接,但这样效果不好。必须确保基础概念清晰,否则遇到变式会无法应对。另外,指针和结构体是难点,需要重点突破。 用户可能的问还包括时间管理,如何在短时间内高效复习。建议每天至少4-6小时,分阶段学习,避免疲劳。同时,错整理和回顾必不可少,避免重复错误。 最后,考试技巧如审、先易后难、注意格式要求(如输入输出格式),这些细节容易丢分,需要提醒用户注意。心态调整也很重要,十天虽然短,但集中精力还是有可能通过的。 总结下来,结构应该是:明确考点、分阶段学习、强化练习、模拟考试、注意细节。同时鼓励用户保持积极心态,坚持每天的计划。</think>十天速成计算机二级C语言需要高效规划和针对性学习。以下是分阶段学习建议: --- ### **第1-3天:基础知识突击** 1. **核心语法** - 数据类型(int, float, char等)与变量声明 - 运算符:算术、关系、逻辑运算符(如`$a += b$`, `$x > y && z < 5$`) - 输入输出函数:`scanf()`、`printf()` - 示例代码: ```c #include <stdio.h> int main() { int a; scanf("%d", &a); printf("a = %d", a); return 0; } ``` 2. **控制结构** - 分支:`if-else`、`switch-case` - 循环:`for`、`while`、`do-while` - 重点练习嵌套结构和流程图绘制。 --- ### **第4-5天:核心编程能力** 1. **数组与字符串** - 一维数组:定义、初始化、遍历(如`$int arr[5] = {1,2,3};$`) - 字符串处理函数:`strlen()`、`strcpy()`等 2. **函数** - 函数定义与调用 - 参数传递(值传递 vs. 地址传递) - 递归函数(如阶乘、斐波那契数列) --- ### **第6-7天:难点突破** 1. **指针** - 指针定义与运算(`$int *p = &a;$`) - 指针与数组的关系(如`$*(arr+i)$`等效于`$arr[i]$`) 2. **结构体与文件操作** - 结构体定义与使用 - 文件读写:`fopen()`、`fscanf()`、`fprintf()` --- ### **第8-10天:真实战与查漏补缺** 1. **历年真** - 每天至少完成2套真,重点分析编程(如排序、查找算法)。 - 示例真片段: ```c // 求1! + 2! + ... + n! int sum = 0, fact = 1; for (int i = 1; i <= n; i++) { fact *= i; sum += fact; } ``` 2. **模拟考试环境** - 使用考试软件(如未来教育、无忧考吧)模拟上机操作。 - 限时完成目,熟悉考试界面和调试流程。 3. **高频易错点整理** - 指针未初始化导致的野指针问 - 数组越界、死循环逻辑错误 - 文件操作后未关闭文件(`fclose()`) --- ### **考试技巧** 1. **审优先** - 先通读目,明确输入输出要求。 2. **分段调试** - 复杂代码分模块编写,逐步验证。 3. **时间分配** - 选择控制在40分钟内,编程留足60分钟。 --- ### **推荐资源** - 教材:《全国计算机等级考试二级教程——C语言程序设计》 - 库:未来教育模拟系统、历年真汇编 - 在线工具:菜鸟教程C语言板块(快速语法查询) --- **注意**:十天速成需每天投入6-8小时,重点攻克高频考点和编程实操。坚持练习+总结,通过率可显著提升!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值