第十六届蓝桥杯省赛PythonB赛道:书架还原之从零开始编写代码

本文是对“非线性结构优化”问题的思路拆解。

一、试题回顾

本题考察:图论


二、还原出原始题意 

        给定一个长度为 n 的排列(所有元素为 1 到 n 的某个顺序排列),你可以交换任意两个位置的元素。你需要计算出将这个排列变为升序排列所需要的最少交换次数。

举个例子:

        排列 = [2, 3, 4, 5, 1]

        目标: 变成 [1, 2, 3, 4, 5],最少交换几次?


三、建模问题

        面对的输入是一个“乱序的排列”,目标是将它变成“升序的排列”;

        “交换任意两个数”,代价是“1 次操作”;

        所以我们希望找到最少的操作步骤。

每个位置应该放什么?

        索引 i 处应放的是 i+1

        所以,不妨把排列视为一个“目标位置错乱”的集合。

如果一个数 x 出现在了不属于它的位置上,会发生什么?

        它原本应该在 x−1的位置上。

        而现在可能是 a[x−1]=y,所以这就意味着:

        数 x 应该去 x−1,但 x−1 处又放着 y,而 y 也不在它该在的地方……

        这是不是在形成一条“路径”或者说“链条”?

本质:一个置换 = 若干个置换环(cycle)

观察这个排列 [2, 3, 4, 5, 1]

 

发现什么了吗?

        这是一个环:

        0 → 1 → 2 → 3 → 4 → 0

这说明,我们可以通过不断交换,把这个环“拆开”成正确位置。

让我们来构造一次环的“还原过程”:

一个长度为 k 的环:a1→a2→⋯→ak→a1

我们希望把每个元素放回它应该在的位置上。

可以使用如下操作:

        固定其中一个元素(比如 a1),然后将其它元素依次换到它们正确的位置上;

        每次操作“修正”一个位置,总共需要 k−1次操作。

        就像拼魔方那样,我们只动一块基准,然后围绕它排好其他块。


四、算法演示

        1.将输入排列看作一个置换;

        2.找到其中所有的置换环(cycle);

        3.对每一个长度为 k 的环,需要 k−1次交换;

        4.将所有环的最小交换次数累加。


五、用图论的视角来重新理解


六、代码呈现

n = int(input())
lst = list(map(int, input().split()))
visited = [False] * n
ans = 0

for i in range(n):
    if not visited[i]:
        cycle_size = 0
        j = i
        while not visited[j]:
            visited[j] = True
            cycle_size += 1
            j = lst[j] - 1
        ans += (cycle_size - 1)

print(ans)  #Ciii^x

AI注释版:

n = int(input())
lst = list(map(int, input().split()))

# 创建一个长度为 n 的布尔型列表 visited,用于标记列表中的元素是否已经被访问过
# 初始时,所有元素都标记为未访问(False)
visited = [False] * n

# 初始化一个变量 ans,用于存储最终的结果
ans = 0

# 遍历列表中的每个元素
for i in range(n):
    # 如果当前元素 i 还未被访问过
    if not visited[i]:
        # 初始化一个变量 cycle_size,用于记录当前循环的大小
        cycle_size = 0
        # 从当前元素 i 开始进行循环遍历
        j = i
        # 当当前元素 j 还未被访问过时,继续循环
        while not visited[j]:
            # 标记当前元素 j 为已访问
            visited[j] = True
            # 循环大小加 1
            cycle_size += 1
            # 更新 j 为 lst[j] - 1,继续在循环中移动
            j = lst[j] - 1
        # 对于每个循环,需要的操作次数为循环大小减 1
        # 将当前循环所需的操作次数累加到结果 ans 中
        ans += (cycle_size - 1)

print(ans)

 


七、拓展

如果我们不允许任意交换,而是只能“相邻交换”呢?

如果只能交换相邻的两个数,那么最小交换次数就等价于逆序对的数量。

也就是说,我们可以通过归并排序的过程来统计

排序类最优交换问题

网络中环检测、最小旋转路径

隐式图结构中的搜索与优化


八、总结

 

作者:Ciiix

物联沃分享整理
物联沃-IOTWORD物联网 » 第十六届蓝桥杯省赛PythonB赛道:书架还原之从零开始编写代码

发表回复