LeetCode 题解工作台

找出分数最低的排列

给你一个数组 nums ,它是 [0, 1, 2, ..., n - 1] 的一个 排列 。对于任意一个 [0, 1, 2, ..., n - 1] 的排列 perm ,其 分数 定义为: score(perm) = |perm[0] - nums[perm[1]]| + |perm[1] - nu…

category

4

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

我们注意到,对于任意一个排列 ,把它循环向左移动任意次,得到的排列分数依然是相同的。由于题目要求返回字典序最小的排列,因此我们可以确定排列的第一个元素一定是 。 另外,由于题目的数据范围不超过 ,我们可以考虑使用状态压缩的方法,来表示当前排列选取的数字集合。我们用一个长度为 的二进制数 来表示当前排列选取的数字集合,其中 的第 位为 表示数字 已经被选取,为 表示数字 还未被选取。

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个数组 nums ,它是 [0, 1, 2, ..., n - 1] 的一个排列 。对于任意一个 [0, 1, 2, ..., n - 1] 的排列 perm ,其 分数 定义为:

score(perm) = |perm[0] - nums[perm[1]]| + |perm[1] - nums[perm[2]]| + ... + |perm[n - 1] - nums[perm[0]]|

返回具有 最低 分数的排列 perm 。如果存在多个满足题意且分数相等的排列,则返回其中字典序最小的一个。

 

示例 1:

输入:nums = [1,0,2]

输出:[0,1,2]

解释:

字典序最小且分数最低的排列是 [0,1,2]。这个排列的分数是 |0 - 0| + |1 - 2| + |2 - 1| = 2

示例 2:

输入:nums = [0,2,1]

输出:[0,2,1]

解释:

字典序最小且分数最低的排列是 [0,2,1]。这个排列的分数是 |0 - 1| + |2 - 2| + |1 - 0| = 2

 

提示:

  • 2 <= n == nums.length <= 14
  • nums[0, 1, 2, ..., n - 1] 的一个排列。
lightbulb

解题思路

方法一:记忆化搜索

我们注意到,对于任意一个排列 perm\textit{perm},把它循环向左移动任意次,得到的排列分数依然是相同的。由于题目要求返回字典序最小的排列,因此我们可以确定排列的第一个元素一定是 00

另外,由于题目的数据范围不超过 1414,我们可以考虑使用状态压缩的方法,来表示当前排列选取的数字集合。我们用一个长度为 nn 的二进制数 mask\textit{mask} 来表示当前排列选取的数字集合,其中 mask\textit{mask} 的第 ii 位为 11 表示数字 ii 已经被选取,为 00 表示数字 ii 还未被选取。

我们设计一个函数 dfs(mask,pre)\textit{dfs}(\textit{mask}, \textit{pre}),表示当前排列选取的数字集合为 mask\textit{mask},且最后一个选取的数字为 pre\textit{pre} 时,得到的排列的最小分数。初始时我们将数字 00 加入到排列中。

函数 dfs(mask,pre)\textit{dfs}(\textit{mask}, \textit{pre}) 的计算过程如下:

  • 如果 mask\textit{mask} 的二进制表示中 11 的个数为 nn,即 mask=2n1\textit{mask} = 2^n - 1,表示所有数字都已经被选取,此时返回 prenums[0]|\textit{pre} - \textit{nums}[0]|
  • 否则,我们枚举下一个选取的数字 cur\textit{cur},如果数字 cur\textit{cur} 还未被选取,那么我们可以将数字 cur\textit{cur} 加入到排列中,此时排列的分数为 prenums[cur]+dfs(mask1<<cur,cur)|\textit{pre} - \textit{nums}[\textit{cur}]| + \textit{dfs}(\textit{mask} \, | \, 1 << \textit{cur}, \textit{cur}),我们需要取所有 cur\textit{cur} 中分数的最小值。

最后,我们利用一个函数 g(mask,pre)\textit{g}(\textit{mask}, \textit{pre}) 来构造得到最小分数的排列。我们首先将数字 pre\textit{pre} 加入到排列中,然后枚举下一个选取的数字 cur\textit{cur},如果数字 cur\textit{cur} 还未被选取,且满足 prenums[cur]+dfs(mask1<<cur,cur)|\textit{pre} - \textit{nums}[\textit{cur}]| + \textit{dfs}(\textit{mask} \, | \, 1 << \textit{cur}, \textit{cur}) 的值等于 dfs(mask,pre)\textit{dfs}(\textit{mask}, \textit{pre}),那么我们就可以将数字 cur\textit{cur} 加入到排列中。

时间复杂度 (n2×2n)(n^2 \times 2^n),空间复杂度 O(n×2n)O(n \times 2^n)。其中 nn 为数组 nums\textit{nums} 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def findPermutation(self, nums: List[int]) -> List[int]:
        @cache
        def dfs(mask: int, pre: int) -> int:
            if mask == (1 << n) - 1:
                return abs(pre - nums[0])
            res = inf
            for cur in range(1, n):
                if mask >> cur & 1 ^ 1:
                    res = min(res, abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur))
            return res

        def g(mask: int, pre: int):
            ans.append(pre)
            if mask == (1 << n) - 1:
                return
            res = dfs(mask, pre)
            for cur in range(1, n):
                if mask >> cur & 1 ^ 1:
                    if abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur) == res:
                        g(mask | 1 << cur, cur)
                        break

        n = len(nums)
        ans = []
        g(1, 0)
        return ans
speed

复杂度分析

指标
时间complexity is O(n * 2^n) due to iterating over all subsets and transitions for each state. Space complexity is O(n * 2^n) to store the DP table, which is feasible for n <= 14.
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Looking for recognition that the problem reduces to cyclic state transition DP with bitmasking.

  • question_mark

    Expecting the candidate to optimize for lexicographically minimal permutation by fixing perm[0].

  • question_mark

    Observing whether the candidate correctly implements DP transitions and reconstruction from bitmask states.

warning

常见陷阱

外企场景
  • error

    Failing to fix perm[0] leading to incorrect lexicographical ordering.

  • error

    Incorrectly updating DP states without considering the cost from previous element.

  • error

    Trying to generate all permutations directly, causing timeouts for n = 14.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Return only the minimum cost instead of the permutation, focusing purely on DP value computation.

  • arrow_right_alt

    Consider a variation with non-cyclic score, requiring handling first and last elements differently.

  • arrow_right_alt

    Extend to larger n using heuristic pruning instead of exact DP to explore trade-offs in performance.

help

常见问题

外企场景

找出分数最低的排列题解:状态·转移·动态规划 | LeetCode #3149 困难