Bootstrap

如何解决pSort问题——Python实现详细解析

一、问题描述

pSort问题描述如下:在一个大小为 nn 的数组中,每个单元格包含一个初始编号(从 1 到 nn),以及一个喜爱编号。在一次操作中,第 ii 个单元格可以与其他单元格交换其值,条件是两者的喜爱编号满足 ∣i−j∣=di|i - j| = d_i。这里 did_i 是第 ii 个单元格的喜爱编号。

我们需要判断能否通过一系列操作,将数组的状态调整为给定的目标状态。

输入

  • 第一行包含整数 nn((1 \leq n \leq 100\)),表示数组的大小。
  • 第二行是一个从 1 到 nn 的排列,表示目标状态。
  • 第三行是喜爱编号数组,包含 nn 个整数。

输出: 如果可以通过合法操作达到目标状态,输出 YES;否则输出 NO


二、示例解析
示例 1

输入

5
5 4 3 2 1
1 1 1 1 1

输出

YES

解析:喜爱编号为 1,意味着每个单元格可以与相邻的单元格交换值。通过若干次交换可以逆转整个数组,满足目标状态。

示例 2

输入

7
4 3 5 1 2 7 6
4 6 6 6 1 1 1

输出

NO

解析:一些单元格受喜爱编号的限制,无法与目标状态对齐。


三、解决方案:DFS实现

为了判断是否可以通过交换操作将数组调整到目标状态,我们可以将问题建模为图的连通性问题:

  1. 每个单元格视为一个节点。
  2. 如果两个节点满足交换条件,则在它们之间建立一条边。
  3. 利用深度优先搜索(DFS),找到每个连通分量,判断目标状态是否符合当前分组。

四、Python代码实现

以下是完整的 Python 实现代码:

def dfs(node, graph, visited, group, root):
    """
    深度优先搜索,将同一连通分量的节点分为一组
    :param node: 当前节点
    :param graph: 邻接表表示的图
    :param visited: 是否访问过
    :param group: 节点组编号
    :param root: 当前连通分量的根节点
    """
    if visited[node]:
        return
    visited[node] = True
    group[node] = root
    for neighbor in graph[node]:
        if not visited[neighbor]:
            dfs(neighbor, graph, visited, group, root)


def main():
    # 输入处理
    n = int(input())  # 数组大小
    target = [0] + list(map(int, input().split()))  # 目标状态
    favorite = [0] + list(map(int, input().split()))  # 喜爱编号

    # 构建图
    graph = [[] for _ in range(n + 1)]
    for i in range(1, n + 1):
        d = favorite[i]
        if i - d > 0:  # 左边邻居
            graph[i].append(i - d)
            graph[i - d].append(i)
        if i + d <= n:  # 右边邻居
            graph[i].append(i + d)
            graph[i + d].append(i)

    # 连通分量标记
    visited = [False] * (n + 1)
    group = [0] * (n + 1)
    for i in range(1, n + 1):
        if not visited[i]:
            dfs(i, graph, visited, group, i)

    # 验证目标状态是否可达
    possible = True
    for i in range(1, n + 1):
        if group[i] != group[target[i]]:
            possible = False
            break

    print("YES" if possible else "NO")


if __name__ == "__main__":
    main()

五、代码详解
1. 图的构建

我们通过一个邻接表 graph 构建图。对于每个单元格 ii:

  • 如果 i−dii - d_i 在数组范围内,则在 ii 和 i−dii - d_i 之间建立边。
  • 如果 i+dii + d_i 在数组范围内,则在 ii 和 i+dii + d_i 之间建立边。
2. 深度优先搜索(DFS)

DFS 用于遍历图的连通分量。每个连通分量分配一个组编号(group),便于后续判断目标状态的可行性。

3. 验证目标状态

对于每个单元格,如果其组编号与目标状态中对应位置的组编号不同,则说明目标状态不可达。


六、复杂度分析
  • 时间复杂度
    • 图的构建需要 O(n)O(n) 时间。
    • DFS 遍历每个节点和边,时间复杂度为 O(n+m)O(n + m),其中 mm 是边的数量。由于 m≤2nm \leq 2n,总复杂度约为 O(n)O(n)。
  • 空间复杂度
    • 图的邻接表占用 O(n)O(n) 空间。
    • 其他辅助数组如 visitedgroup 也需要 O(n)O(n) 空间。

总的来说,时间和空间复杂度均为 O(n)O(n)。


七、测试用例

测试用例 1

输入:
5
5 4 3 2 1
1 1 1 1 1
输出:
YES

测试用例 2

输入:
7
4 3 5 1 2 7 6
4 6 6 6 1 1 1
输出:
NO

测试用例 3

输入:
7
4 3 5 2 1 7 6
4 6 6 6 1 1 1
输出:
YES

八、总结

通过将问题建模为图的连通性问题,我们利用 DFS 有效地解决了 pSort 问题。该解决方案不仅直观易懂,而且在处理 nn 较大的情况下依然高效。

如果本文对你有所帮助,记得点赞和收藏!欢迎在评论区分享你的优化思路或其他解决方法!

;