首页 > 其他分享 >2023-12-15 每天一练

2023-12-15 每天一练

时间:2023-12-15 22:23:33浏览次数:33  
标签:12 15 val nums self 2023 PATH 最优 节点

依据灵神的网站题目顺序 + 每日一题

2789. 合并后数组中的最大元素

问题

给你一个下标从 0 开始、由正整数组成的数组 nums

你可以在数组上执行下述操作 任意 次:

选中一个同时满足 0 <= i < nums.length - 1nums[i] <= nums[i + 1] 的整数 i 。将元素 nums[i + 1] 替换为 nums[i] + nums[i + 1] ,并从数组中删除元素 nums[i]

返回你可以从最终数组中获得的 最大 元素的值。

解答

这其实很贴近 状态转移:向一个状态输入一个操作,对数据产生影响,得到下一个状态。所以看成是一个 自动机。

而 自动机 的主要组成便是:状态集、状态转移函数、操作、初始状态集、终止状态集。我们可以列出这些参数,以便从中得到破局之法

显然对于 nums[i] 的操作只有两种:不操作、与 nums[i + 1] 相加

初始状态已被题目给定

终止状态即我们的函数结束后会得到一个什么样的数组呢,显然,它是一个递减的数组,否则可以继续比较。

我们便由其终止状态可以得出我们算法:贪心。朴素的思想是从后往前遍历,遇到比自己小的就加上它,然后把该和当作自己的值,继续遍历;若遇到比自己大的,说明从自己往后的子数组已经是递减的了,而往前的子数组在遍历后的最小的值也必然比自己大,说明子结构已经达到最优,此时让自己的值变成前一个比自己大的值即可

贪心的运用需要证明(其实 动态规划 的使用也要证明,只不过我们往往忽略它):

  1. 最优子结构(一个问题的最优解包含其子问题的最优解)的一般性证明:
    1. 证明问题最优解的第一个组成部分是做出一个选择
    2. 对于一个给定问题,在其可能的第一步选择中,你假定已经知道哪种选择才会得到最优解。你现在并不关心这种选择具体是如何得到的,只是假定已经知道了这种选择。
    3. 给定可获得最优解的选择后,你确定这次选择会产生哪些子问题,以及如何最好地刻画子问题空间。
    4. 利用“剪切—粘贴”(cut-and-paste) 技术证明:作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解。证明这一点是利用反证法:假定子问题的解不是其自身的最优解,那么我们就可以从原问题的解中“剪切”掉这些非最优解,将最优解“粘贴”进去,从而得到原问题一个更优的解,这与最初的解是原问题最优解的前提假设矛盾。如果原问题的最优解包含多个子问题,通常它们都很相似,我们可以将针对一个子问题的“剪切—粘贴”论证方法稍加修改,用于其他子问题。
  2. 贪心选择(将子问题的最优解和贪心选择组合在一起就可以生成整个问题的最优解)的一般性证明:
    通过时间或者空间的局部最优推出全局最优. 也是剪切粘贴法.
    或者是假设某个全局最优解一定包含某个局部最优解
    设某个全局最优解不包含局部最优解, 去掉一个局部解, 将局部最优解加入, 判断前后变化
    找到 集合中元素的比较指标

动态规划需要满足 最优子问题 性质,否则不可用
动态规划可能会遇到 重叠子问题 问题,据此可以使用记忆化搜索进行优化
对于不同问题领域,最优子结构的不同体现在两个方面:

  1. 原问题的最优解中涉及多少个子问题,以及
  2. 在确定最优解使用哪些子问题时,我们需要考察多少种选择。
    贪心便是选择能带来最优的策略,以减少考察次数
    贪心算法在进行第一次选择之前不求解任何子问题。一个动态规划算法是自底向上进行计算的,而一个贪心算法通常是自顶下的,进行一次又一次选择,将给定问题实例变得更小。

对于这个问题来说:

  1. 最优子结构 是 递减的子数组,即上一个状态。
  2. 因为本题就只有一种操作,即遇大则加操作,所以我们贪心选择就是 遇大则加

本题的证明:

  1. 最优子结构:
    假设某一个最优解得到的数组不是递减的数组,那么其中必然存在 i 满足 nums[i] <= nums[i + 1],那么我们和依旧可以执行操作,使其相加替换,并向前遍历,最后得到的最大值可能大于假设中的最优解,那么这与假设矛盾,故而该问题的最优子结构就是 递减的数组
  2. 贪心选择:
    假设某一个最优解的操作策略和贪心的操作策略不同,由于本题就只有一种操作,所以这不可能,所以他们的操作策略相同,所以我们的贪心能够得到最优解

代码:

class Solution:
    def maxArrayValue(self, nums: List[int]) -> int:
        tmp = nums[-1]
        for i in range(len(nums) - 2, -1, -1):
            if nums[i] <= tmp:
                tmp += nums[i]
            else:
                tmp = nums[i]
        return tmp

2790. 长度递增组的最大数目

问题

给你一个下标从 0 开始、长度为 n 的数组 usageLimits

你的任务是使用从 0 到 n - 1 的数字创建若干组,并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外,还必须满足以下条件:

  • 每个组必须由 不同 的数字组成,也就是说,单个组内不能存在重复的数字。
  • 每个组(除了第一个)的长度必须 严格大于 前一个组。

在满足所有条件的情况下,以整数形式返回可以创建的最大组数。

解答

构造出一个 1 + 2 + ··· 的结构出来即可
后面少的向前面借

class Solution:
    def maxIncreasingGroups(self, usageLimits: List[int]) -> int:
        usageLimits.sort()
        ans = 0
        tmp = 0
        for i in usageLimits:
            tmp += i
            if tmp >= (ans + 2) * (ans + 1) // 2:
                ans += 1
        return ans

2415. 反转二叉树的奇数层

问题

给你一棵 完美 二叉树的根节点 root ,请你反转这棵树中每个 奇数 层的节点值。

例如,假设第 3 层的节点值是 [2,1,3,4,7,11,29,18] ,那么反转后它应该变成 [18,29,11,7,4,3,1,2]
反转后,返回树的根节点。

完美 二叉树需满足:二叉树的所有父节点都有两个子节点,且所有叶子节点都在同一层。

节点的 层数 等于该节点到根节点之间的边数。

解答

该题比较简单,使用广度优先搜索即可

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        depth = 0
        q = [root]
        while q:
            if depth % 2 == 1:
                c = 1 << depth
                for i in range(c >> 1):
                    q[i].val, q[c - i - 1].val = q[c - i - 1].val, q[i].val
            tmp = q
            q = []
            for node in tmp:
                if node.left == None:
                    return root
                q.append(node.left)
                q.append(node.right)
            depth += 1

当然也可以使用 深度优先搜索

众所周知,所谓 树 和 图 中的 递归 都不过是对 前序遍历,中序遍历,后序遍历的推广

不过本体其实哪种遍历都可,只是要将整棵树分成两半,分别同时进行遍历,所以在递归时,他们永远在同一层,为了对称,左右遍历的时候需要反向,即 dfs(node1.left, node2.right)dfs(node1.right, node2.left)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        def dfs(node1, node2, is_odd):
			if node1 == None:
				return
			if is_odd:
				node1.val, node2.val = node2.val, node1.val
			dfs(node1.left, node2.right, not is_odd)
			dfs(node1.right, node2.left, not is_odd)
		dfs(root.left, root.right, True)
		return root

2791. 树中可以形成回文的路径数

题目

给你一棵 树(即,一个连通、无向且无环的图),根 节点为 0 ,由编号从 0 到 n - 1n 个节点组成。这棵树用一个长度为 n 、下标从 0 开始的数组 parent 表示,其中 parent[i] 为节点 i 的父节点,由于节点 0 为根节点,所以 parent[0] == -1

另给你一个长度为 n 的字符串 s ,其中 s[i] 是分配给 iparent[i] 之间的边的字符。s[0] 可以忽略。

找出满足 u < v ,且从 uv 的路径上分配的字符可以 重新排列 形成 回文 的所有节点对 (u, v) ,并返回节点对的数目。

如果一个字符串正着读和反着读都相同,那么这个字符串就是一个 回文 。

解答

如何思索(主要解释笔者当时的想法,引导思考,未必正确!!!!):
首先看题,题目要求的是满足条件的个数,那么一次搜索就两种状态,要么 True,要么 False,那么我们可以先观察一次搜索中 自动机 的情况。自动机的输入是字符树上的节点,它的状态是 字符串,或者说是 节点A 到 节点B 的路径,终止条件是 这个状态的字符串可以组合成 回文,此时便可以考虑能用的 回文特性,以及 树的路径特点:

  1. 可以通过重新排列组合成 回文串 的 字符串 的特性:
    1. 字符在字符串中出现的字数为0,1以及偶数次,且整个字符串中最多只有一个字符能出现一次,若多个字符只出现一次,那么,不论该字符串如何排列,都不会形成回文串,因为它需要轴对称
    2. 由此,我们可以记录 节点A 到 节点B 的路径中,字符出现的字数,来判断是否形成回文串
  2. 节点A 到 节点B 的路径的特点:
    1. 节点A 到 节点B 的路径必然经过他们的 最近公共祖先(LCA)节点,再结合 树上差分/前缀和 的知识,可以知晓 \(PATH_{AB} = PATH_{RA} - PATH_{R LCA_{AB}} + PATH_{RB} - PATH_{R LCA_{AB}}\)。据此,可以先求 根节点 \(R\) 到其他节点的路径中经过的各字符的个数,最后再使用上述公式进行操作,但是这样的内存空间太大,需要进行压缩
    2. 注意到 字符 出现的次数要么是奇数次,要么是偶数次,而且最多一个奇数次。所以我们可以用 0 表示一个字符出现偶数次,用 1 表示其出现奇数次。那么 26 个字符只需 26 bit 即可标识,即 int 类型。只要字符串对应的 26 bit 中有 多个 1 那么就可以放弃。而且据此,可以将上述公式化简为 \(PATH_{AB} = (PATH_{RA} \oplus PATH_{R LCA_{AB}}) \oplus (PATH_{RB} \oplus PATH_{R LCA_{AB}}) = PATH_{RA} \oplus PATH_{RB}\),所以本体化为求 \(PATH_{AB} = 0 , 1 << i, i \in [0, 25]\)
    3. 更多的位运算应用可以查看这篇文章:从集合论到位运算,常见位运算技巧分类总结!
  3. 由上,最后再进行深度优先搜索即可
class Solution:
    def countPalindromePaths(self, parent: List[int], s: str) -> int:
        n = len(s)
        g = [[] for _ in range(n)]
        for i in range(1, n):
            g[parent[i]].append(i)

        cnt = Counter([0])  # 一条「空路径」
        def dfs(v: int, xor: int) -> int:
            res = 0
            for w in g[v]:
                bit = 1 << (ord(s[w]) - ord('a'))
                x = xor ^ bit
                res += cnt[x] + sum(cnt[x ^ (1 << i)] for i in range(26))
                cnt[x] += 1
                res += dfs(w, x)
            return res
        return dfs(0, 0)

标签:12,15,val,nums,self,2023,PATH,最优,节点
From: https://www.cnblogs.com/SpiritiualWander/p/17904284.html

相关文章

  • 2023-2024-1 20231405《计算机基础与程序设计》第十二周学习总结
    2023-2024-120231405《计算机基础与程序设计》第十二周学习总结作业信息作业属于哪个课程https://edu.cnblogs.com/campus/besti/2023-2024-1-CFAP作业要求在哪里https://edu.cnblogs.com/campus/besti/2023-2024-1-CFAP/homework/13009作业的目标自学......
  • 12.15日记
    log4j.rootLogger=info,consolePrint,errorFile,logFile log4j.appender.consolePrint.Encoding=UTF-8log4j.appender.consolePrint=org.apache.log4j.ConsoleAppenderlog4j.appender.consolePrint.Target=System.outlog4j.appender.consolePrint.layout=org.apache.l......
  • 12月集训游记(day1-day3)
    Day1好好好,今天没有爆零,这真是一个良好的开局,接下来的集训我一定会学有所得的哈哈哈哈哈哈哈哈哈…总结一下今天的题目T1反正是个动态规划首先,怎么看出来这是个动态规划的……因为计数问题不是组合数就是dp,而显然,如果这道题存在组合数做法我更不会......
  • 2023.12.15——每日总结
    学习所花时间(包括上课):9h代码量(行):0行博客量(篇):1篇今天,上午学习,下午学习;我了解到的知识点:1.c#明日计划:学习......
  • 12.15
    最后20分钟写个闲话今天没啥,调一道题调了一晚上......
  • 12月集训游记
    Day1好好好,今天没有爆零,这真是一个良好的开局,接下来的集训我一定会学有所得的哈哈哈哈哈哈哈哈哈…总结一下今天的题目T1反正是个动态规划首先,怎么看出来这是个动态规划的……因为计数问题不是组合数就是dp,而显然,如果这道题存在组合数做法我更不会显然......
  • 闲话12.15
    今天打了一场模拟赛,垫底了。T1找了两个小时的性质,没找到性质,寄。也没一点暴力分,有了性质基本就是100pts了,矩阵加速比较裸。T2T3已经没时间看了,就摆了,打了15pts就跑了。最终得分15pts,rk70多吧。越来越拉了呢。下午花了一个半小时改T4,有半个小时都是因为没开ll在调......
  • 2023-2024-1 20231406 《计算机基础与程序设计》第十二周学习总结
    2023-2024-120231406《计算机基础与程序设计》第十二周学习总结作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第十二周作业这个作业的目标自学《C语言程序设计》第11章并完成云班课测试......
  • 12.15 闲话
    今天特别水基本没有学术昨天忘更闲话了学校奇怪规定,本地的可以回家但是外地不行幸好我是秦皇岛的不然就能回去了,哦哦原来我家长来了我又能走了推歌世末歌者蝉时雨化成淡墨渲染暮色渗透着勾勒出足迹与车辙欢笑声与漂浮的水汽饱和隔着窗同城市一并模糊了拨弄着旧吉......
  • 从嘉手札<2023-12-15>
    荒原 朔方2023.12.15人生实属是很愁的时间愁到听不见一点雪花飘落的声音愁到连随便写点文章都算得上拼尽全力萧瑟的北风吹散了为数不多的倔强漫天的雪花飞舞埋葬的是那么多年走过的春秋时光的幻象影影绰绰将就地留下一滴泪水在无边无际的茫茫大雪中我仿佛看到素白的......