欢迎光临
我们一直在努力

【算法进阶】从动态规划到置换环:三道经典题带你贯穿算法核心思维

目录

1. A · B Problem

题目链接

解题思路

Python 代码实现

2.园艺

 题目链接

解题思路

Python 代码实现

1.暴力实现(8个用例会超时)

2.动态规划

3.书架还原

题目链接

解题思路

Python 代码实现


1. A · B Problem

输入格式

输入的第一行包含一个正整数 L,表示题目描述中的限制条件。

输出格式

输出一行包含一个整数表示答案。

样例输入 1

2

样例输出 1

1

样例输入 2

3

样例输出 2

5

题目链接

0A · B Problem – 蓝桥云课

解题思路

1. 问题转化:把向量内积拆成两个独立的乘积和

        原式 = u+v≤L

2. 预处理:计算每个数的有序因数对数量

        定义了数组 yinshu[],其中 yinshu[k] 表示满足 x*y=k 的正整数有序对 (x,y) 的数量

        eg: k=12 时,有序对有 (1,12),(2,6),(3,4),(4,3),(6,2),(12,1),共 6 对,所以 yinshu[12] = 6

3. 累加所有合法组合

        遍历所有可能的 u(从 1 到 L−1)

        对每个 u,v 的取值范围是 1 到 L−u

        每一组 (u,v) 对应的合法向量对数量是 yinshu[u] * yinshu[v]

        把所有这些值累加起来,就是最终的答案

Python 代码实现

import os
import sys

# 请在此输入您的代码
l = int(input())
# l = (ac)+(bd)
# l = u + v

yinshu = [0] * (l+1)
for i in range(1,l+1): # 因数
for j in range(i,l+1,i): # 被除数,i 是 j 的因数
yinshu[j]+=1 # j的因数个数加1,因子个数 = (a,c) 有序对个数
# 12=1×12=2×6=3×4=4×3=6×2=12×1
# (1,12)
# (2,6)
# (3,4)
# (4,3)
# (6,2)
# (12,1)

ans = 0
for u in range(1,l):
maxv = l-u
for v in range(1,maxv+1):
ans += yinshu[u] * yinshu[v]
print(ans)

2.园艺

问题描述

小蓝从左到右种了 n 棵小树,第 i 棵树的高度为 hi​,相邻树的间隔相同。

小蓝想挪走一些树,使得剩下的树等间隔分布,且从左到右高度逐渐上升(相邻两棵树高度满足右边的比左边的高),小蓝想知道最多能留下多少棵树。

输入格式

输入的第一行包含一个正整数 nn。

第二行包含 nn 个正整数 h1,h2,…,hn,相邻整数之间使用一个空格分隔。

输出格式

输出一行包含一个整数,表示最多能留下的树的数量。

样例输入

6
3 5 4 7 6 7

样例输出

3

样例说明

留下第 11、33、55 棵树,它们等间隔且从左到右高度逐渐上升。

 题目链接

0园艺 – 蓝桥云课

解题思路

题目核心:寻找原序列中下标成等差数列、高度成严格递增的最长子序列

这道题的难点在于“等间隔”。一旦公差 $d$ 固定,原序列就被拆分成了 $d$ 条独立的“轨道”。我们只需要在每条轨道上统计连续上升的段长度即可。

  • 暴力突破:枚举所有可能的公差 d[1, n]。

  • 线性优化:对于固定的 d,利用“贪心+模拟”的思想,遍历所有轨道。

Python 代码实现

1.暴力实现(8个用例会超时)

import os
import sys

# 请在此输入您的代码
n = int(input())
h = list(map(int,input().split()))

max_count = 1
# 遍历间隔
for d in range(1,n):
# 遍历起始位置
for start in range(d):
cur = 1
# 在当前轨道遍历
for i in range(start,n,d):
if h[i] > h[i-d]:
cur += 1
else:
max_count = max(max_count,cur)
cur = 1 # 到当前截断,后面的数字依然还要计算最大
# 每一条轨道跑完,都要更新一次最大值
max_count = max(max_count,cur)
if n//d+1 <= max_count: # 数量不足剪枝
pass

print(max_count)

2.动态规划

暴力框架里,我们写了 for start in range(d)。但在你这段代码里,我们直接 for i in range(d, n)。这是为什么?

因为每一个 i 都在查找它的“前任” i-d:

  • 如果 h[i] > h[i-d]:

    说明当前这棵树可以完美地接在 i-d 后面,形成更长的上升序列。那么长度就是:

    counts[i] = counts[i-d] + 1

    这就好比你在接力赛,你接过了 i-d 手里的棒,并在他的基础上又跑了一程。

  • 如果 h[i] <= h[i-d]:

    虽然间隔是对的,但高度不符合“严格递增”。这时候 i 不能接在 i-d 后面,它只能“自立门户”,长度重置为 1。由于初始化已经是 1 了,所以这里不需要写

  • import os
    import sys

    # 请在此输入您的代码
    n = int(input())
    h = list(map(int, input().split()))
    max_count = 1

    # 枚举公差 d
    for d in range(1, n):
    # 优化:如果剩余长度不足,提前退出
    if n // d + 1 <= max_count: break

    # 使用 dp 在轨道上扫描
    counts = [1] * n
    for i in range(d, n):
    if h[i] > h[i-d]:
    # 以第i棵树结尾,且公差为d的等间隔上升序列,目前最长是多少
    counts[i] = counts[i-d] + 1
    max_count = max(max_count, counts[i])
    print(max_count)

    3.书架还原

    问题描述

    在一个偏远的图书馆里,有个书架上放着 NN 本书,每本书上都标有一个唯一的编号,从 11 到 NN。

    按照规矩,这些书应该按编号从小到大依次排列:11 号书位于最左端,22 号书紧随其后,以此类推,直到 NN 号书在最右端。这样的顺序不仅看起来整齐,也方便读者快速找到想借的书。

    可昨天店里人来人往,借书还书忙得不可开交,书架上的顺序出现了错乱。现在,书架上的书变成了 A=(A1,A2,…,ANA=(A1​,A2​,…,AN​),其中 AiAi​ 表示第 ii 个位置上的书编号。

    管理员决定动手整理书架,但时间有限,他希望用最少的操作把书的顺序恢复到正确的排列。每次操作,他可以挑选书架上任意两本书,交换它们的位置。例如,如果当前排列是 (3,1,2)(3,1,2),他可以交换第 11 本和第 22 本,得到 (1,3,2)(1,3,2),再交换第 22 本和第 33 本,得到 (1,2,3)(1,2,3)。

    你的任务是帮助管理员计算,最少需要进行多少次操作,才能让书架上的书的编号排列变为 (1,2,…,N)(1,2,…,N)。

    输入格式

    第一行包含一个整数 NN,表示书架上书的总数。

    第二行包含 NN 个空格分隔的整数 A1,A2,…,ANA1​,A2​,…,AN​,表示当前书架上每本书的编号。A1,A2,…,ANA1​,A2​,…,AN​ 是一个 11 到 NN 的随机排列。

    输出格式

    输出一个整数,表示将书架上的书恢复到正确排列所需的最少操作次数。

    样例输入

    3
    3 1 2

    样例输出

    2

    题目链接

    0书架还原 – 蓝桥云课

    解题思路

    题目核心:将一个乱序排列通过最少次数的“两两交换”恢复成递增序列。

    这是一个典型的图论模型——置换环。

    • 找环:每个元素 Ai 都有一个它应该去的“正确位置”。我们将当前位置指向它目标位置,最终会形成若干个封闭的环。

    • 结论:对于一个长度为 k 的环,将其内部元素全部归位需要 k-1 次交换。

    • 公式:总操作次数 = sum (ki – 1) = N – (环的个数)。

    Python 代码实现

    import os
    import sys

    # 请在此输入您的代码
    inputt = sys.stdin.read().split()
    n = int(inputt[0])
    a = [0] + list(map(int,inputt[1:]))
    visted = [False] * (n+1)
    cnt = 0
    for i in range(1,n+1):
    if not visted[i]:
    # 发现一个新的环
    cnt += 1
    curr = i
    # 样例:3 1 2
    # 环:位置1 -> 书号3 -> 去位置3 -> 书号2 -> 去位置2 -> 书号1 -> 回到位置1
    while not visted[curr]:
    visted[curr] = True
    curr = a[curr]

    # 总的最少交换次数 = (环1长度-1) + (环2长度-1) + …
    # 简化公式:最少交换次数 = 总书数n – 环的数量
    print(n-cnt)

    赞(0)
    未经允许不得转载:171主机测评 » 【算法进阶】从动态规划到置换环:三道经典题带你贯穿算法核心思维
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址