算法题解博客:三道经典题目的思路与实现

一、游游的重组偶数(数学 + 贪心)

题号 :10274325 | 难度 :中等 | 标签:数学、贪心、字符串处理

题目描述

游游拿到一个正整数,希望重排其数位使其变为偶数(无oles前导零) 。若有合法解输出任意重排后的偶数,否则输出 -1。共 q次询问。

输入:第一行 q(询问次数,1≤q≤1e5),接下来 q行每行一个正整数 x1≤x≤1e9)。

输出:q行,每行对应一次询问的结果。

解题思路(贪心 + 数学)

偶数的核心性质是最后一位能被 2 整除。因此,我们需要:

  1. 检查是否存在偶数位 :若所有数位都是奇数,直接输出 -1

  2. 构造合法偶数

    • 优先将最小的非零偶数位放在最后(保证无oles前导零?不,最后一位是偶数的话,前导零由高位决定。正确逻辑是:最后一位选一个偶数,剩下的数位按从小到大排列(或任意顺序,只要高位非oles零)。

    • 注意:若原数的数位全为奇数,无法构造偶数,输出 -1

代码实现(Python)

复制代码
import sys

def solve(x):
    s = list(str(x))
    # 统计偶数位(能被2整除的数位)
    even_digits = [c for c in s if int(c) % 2 == 0]
    if not even_digits:
        return -1
    # 尝试构造:选一个偶数作为最后一位,剩下的数位排序(避免前导零)
    for e in even_digits:
        temp = s.copy()
        temp.remove(e)
        temp.sort()  # 升序,保证高位尽可能小(避免前导零)
        # 检查是否前导零:若temp为空(x是一位数,且是偶数),则直接返回e;否则看第一位是否非0
        if not temp:
            return e
        if temp[0] != '0':
            return ''.join(temp) + e
        # 若temp[0]是0,说明剩下的数位全是0?不可能,因为x是正整数,至少有一个非零数位(除了x=0,但x≥1)
        # 比如x=20,s=['2','0'], even_digits=['2','0']。选e='0'时,temp=['2'], 排列后['2']+['0']='20';选e='2'时,temp=['0'],排列后['0']+['2']='02'(前导零,非法)。所以优先选最小的非零偶数?
        # 优化:选最小的非零偶数作为最后一位?或者遍历所有偶数位,找第一个能使剩余数位无oles前导零的。
    # 理论上上面的循环会找到,因为even_digits非空,且x是正整数(至少有一个非零数位)
    # 兜底(其实不会到这里)
    return -1

q = int(sys.stdin.readline())
for _ in range(q):
    x = sys.stdin.readline().strip()
    res = solve(x)
    print(res if res != -1 else -1)

测试用例

  • 输入 3,查询 1312324

    • 13:数位 1,3无偶数,输出 -1

    • 123:偶数位 2,剩余 1,3排序为 13,拼接为 132

    • 24:偶数位 2,4,选 4的话剩余 224;选 2的话剩余 442,但题目允许任意合法解,这里输出 24(原样也可,因为24是偶数)。


二、体操队形(DFS + 枚举 + 剪枝)

题号 :2227705 | 难度 :中等 | 标签:DFS、回溯、剪枝、排列组合

题目描述

体操队有 n个同学(标号 1~n),每个同学 i有一个诉求 a[i](表示想排在 a[i]号同学前面;若 a[i]=i则无位置需求)。求满足所有诉求的队形方案数。

输入:第一行 n2≤n≤10),第二行 n个整数 a[1..n]1≤a[i]≤n)。

输出:方案数。

解题思路(DFS + 回溯 + 剪枝)

这是典型的排列约束问题 ,可用 DFS 枚举所有排列,同时用剪枝排除不满足约束的情况。

  • 状态表示:当前已选的队员集合(用位掩码或数组标记)、当前队形的最后一个队员。

  • 约束条件 :对于当前要加入的队员 u,其诉求 a[u]必须已经出现在队形中(或 a[u]=u,即无约束)。

  • 剪枝 :若当前队员 ua[u]不在已选集合中,且 a[u]≠u,则跳过该队员。

代码实现(Python)

复制代码
def count_formation(n, a):
    used = [False] * (n + 1)  # 标记是否已选(队员标号1~n)
    count = 0

    def dfs(path):
        nonlocal count
        if len(path) == n:
            count += 1
            return
        for u in range(1, n + 1):
            if not used[u]:
                # 检查约束:a[u]必须在path中,或者a[u]==u
                if a[u-1] == u or (used[a[u-1]]):  # a是0-based输入,u是1-based
                    used[u] = True
                    path.append(u)
                    dfs(path)
                    path.pop()
                    used[u] = False

    dfs([])
    return count

n = int(input())
a = list(map(int, input().split()))
print(count_formation(n, a))

测试用例(示例1)

输入:

复制代码
3
1 1 2

解释:

  • 队员1诉求:排在自己前面(无约束);队员2诉求:排在1前面;队员3诉求:排在2前面。

  • 可能的排列:必须满足 21前,32前 → 唯一排列是 3→2→1?不对,示例输出是 1,但实际逻辑可能需要重新分析。哦,示例输入是 1 1 2,即:

    • 队员1(u=1)的a[0]=1 → 无约束;

    • 队员2(u=2)的a[1]=1 → 必须排在1前面;

    • 队员3(u=3)的a[2]=2 → 必须排在2前面。

      所以合法排列只能是 3→2→1?但示例输出是 1,和代码逻辑一致。


三、二叉树中的最大路径和(树形DP)

题号 :622 | 难度 :困难 | 标签:树形DP、二叉树、深度优先搜索

题目描述

二叉树中的路径定义为:从任意节点出发,经过父→子或子→父连接,到达任意节点的序列(同一节点最多出现一次,路径至少一个节点,不一定过根)。给定二叉树的根节点 root,求最大路径和。

要求:时间复杂度 O(n),空间复杂度 O(1)(递归栈空间除外)。

解题思路(树形DP)

树形DP的核心是后序遍历,计算每个节点的"最大贡献值"(以该节点为起点,向下延伸的最大单链和),并维护全局最大路径和。

  • 状态定义dfs(node)返回以 node为起点的最大单链和(可以向左或向右,取正贡献)。

  • 全局最大路径和:在每个节点,计算"左贡献 + 右贡献 + 节点值"(经过该节点的最大路径和),并更新全局最大值。

  • 剪枝:若子树的贡献为负,则舍弃(取0,因为负数会减少总和)。

代码实现(Python)

复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def maxPathSum(self, root: TreeNode) -> int:
        self.max_sum = float('-inf')  # 全局最大路径和

        def dfs(node):
            if not node:
                return 0
            # 递归计算左右子树的最大单链和(负贡献则取0,因为不选该子树)
            left = max(dfs(node.left), 0)
            right = max(dfs(node.right), 0)
            # 经过当前节点的最大路径和(左+右+当前值)
            current_max = node.val + left + right
            self.max_sum = max(self.max_sum, current_max)
            # 返回以当前节点为起点的最大单链和(只能选左或右,否则路径会分叉)
            return node.val + max(left, right)

        dfs(root)
        return self.max_sum

测试用例(示例)

输入二叉树:

复制代码
1
   / \
  2   3
  • 左子树贡献:max(dfs(2),0)=2(dfs(2)返回2,因为2的左右子树为空,贡献0+0+2=2?不,dfs(2)的逻辑是:左和右都是0,返回2+max(0,0)=2。

  • 右子树贡献:max(dfs(3),0)=3

  • 当前节点(1)的路径和:1+2+3=6,全局最大更新为6。

  • 返回以1为起点的单链和:1+max(2,3)=4

    最终输出 6,与示例一致。


总结

这三道题分别覆盖了数学贪心DFS回溯剪枝树形DP三种经典算法思路:

  • 游游的重组偶数:利用偶数性质+贪心构造,注意前导零问题。

  • 体操队形:通过DFS枚举排列,结合约束条件剪枝,高效求解方案数。

  • 二叉树最大路径和:树形DP的经典应用,后序遍历+全局变量维护最大路径。

每道题的核心都在于理解问题本质,并选择合适的数据结构和算法策略(贪心、回溯、DP)来高效解决。

相关推荐
算法-大模型备案 多米2 小时前
大模型备案实操指南:材料、流程与避坑要点
大数据·网络·人工智能·算法·文心一言
顾温2 小时前
数据转换函数
开发语言·算法
汉克老师2 小时前
GESP2025年6月认证C++三级( 第三部分编程题(1、奇偶校验)
c++·算法·gesp三级·gesp3级·按位操作
Fcy6482 小时前
算法基础详解(一)模拟算法与高精度算法
算法·模拟算法·高精度算法
Promise微笑3 小时前
算法对齐还是实战突围?解构GEO优化中方法论与实践的权重博弈
算法
米粒13 小时前
力扣算法刷题 Day 29
算法·leetcode·职场和发展
wfbcg3 小时前
每日算法练习:LeetCode 125. 验证回文串 ✅
算法·leetcode·职场和发展
We་ct3 小时前
LeetCode 295. 数据流的中位数:双堆解法实战解析
开发语言·前端·数据结构·算法·leetcode·typescript·数据流
Aaron15883 小时前
RFSOC+VU13P/VU9P+GPU通用一体化硬件平台
人工智能·算法·fpga开发·硬件架构·硬件工程·信息与通信·基带工程