You are given an array nums
which is a permutation of [0, 1, 2, ..., n - 1]
. The score of any permutation of [0, 1, 2, ..., n - 1]
named perm
is defined as:
score(perm) = |perm[0] - nums[perm[1]]| + |perm[1] - nums[perm[2]]| + ... + |perm[n - 1] - nums[perm[0]]|
Return the permutation perm
which has the minimum possible score. If multiple permutations exist with this score, return the one that is lexicographically smallest among them.
For example:
nums = [1,0,2]
Output: [0,1,2]
Explanation:
The lexicographically smallest permutation with minimum cost is [0,1,2]
. The cost of this permutation is |0 - 0| + |1 - 2| + |2 - 1| = 2
.
Another example:
nums = [0,2,1]
Output: [0,2,1]
Explanation:
The lexicographically smallest permutation with minimum cost is [0,2,1]
. The cost of this permutation is |0 - 1| + |2 - 2| + |1 - 0| = 2
.
Constraints:
2 <= n == nums.length <= 14
nums
is a permutation of [0, 1, 2, ..., n - 1]
.The most straightforward approach is to generate all possible permutations of the input array nums
and calculate the score for each permutation. We keep track of the minimum score encountered so far and the lexicographically smallest permutation that achieves this minimum score.
Algorithm:
[0, 1, 2, ..., n - 1]
.perm
, calculate its score as |perm[0] - nums[perm[1]]| + |perm[1] - nums[perm[2]]| + ... + |perm[n - 1] - nums[perm[0]]|
.min_score
to store the minimum score seen so far, initialized to infinity.best_perm
to store the lexicographically smallest permutation with the minimum score.min_score
, update min_score
and best_perm
.min_score
, update best_perm
only if the current permutation is lexicographically smaller than best_perm
.best_perm
.Code (Python):
import itertools
def calculate_score(perm, nums):
score = 0
n = len(nums)
for i in range(n):
score += abs(perm[i] - nums[perm[(i + 1) % n]])
return score
def min_permutation_score_brute_force(nums):
n = len(nums)
indices = list(range(n))
permutations = itertools.permutations(indices)
min_score = float('inf')
best_perm = None
for perm in permutations:
perm = list(perm)
score = calculate_score(perm, nums)
if score < min_score:
min_score = score
best_perm = perm
elif score == min_score:
if perm < best_perm:
best_perm = perm
return best_perm
Big O Runtime: O(n! * n), where n is the length of the input array. Generating all permutations takes O(n!), and calculating the score for each permutation takes O(n).
Big O Space: O(n), where n is the length of the input array. This is due to the space required to store the current permutation.
Since the constraint 2 <= n <= 14
is relatively small, we can optimize the solution using dynamic programming with bitmasking. The main idea is to use a bitmask to represent which indices have already been included in the current partial permutation.
Algorithm:
dp[mask][last]
where mask
is a bitmask representing the set of used indices and last
is the last index used in the permutation. dp[mask][last]
stores the minimum cost to build a partial permutation using indices represented by mask
and ending with index last
.dp[1 << i][i] = 0
for all i
from 0
to n - 1
.1
to (1 << n) - 1
.i
that are included in the mask (mask & (1 << i)
is true).i
, iterate through all possible next indices j
that are not included in the mask (mask & (1 << j)
is false).dp[mask | (1 << j)][j] = min(dp[mask | (1 << j)][j], dp[mask][i] + abs(i - nums[j]))
.i
that can be the second to last node in a minimal cycle, and then iterate through all possible j
which represents the last node in the optimal cycle.Code (Python):
import sys
def min_permutation_score_dp(nums):
n = len(nums)
dp = [[float('inf')] * n for _ in range(1 << n)]
parent = [[None] * n for _ in range(1 << n)]
for i in range(n):
dp[1 << i][i] = 0
for mask in range(1, 1 << n):
for i in range(n):
if mask & (1 << i):
for j in range(n):
if not (mask & (1 << j)):
new_mask = mask | (1 << j)
cost = dp[mask][i] + abs(i - nums[j])
if cost < dp[new_mask][j]:
dp[new_mask][j] = cost
parent[new_mask][j] = i
min_cost = float('inf')
start_node = -1
second_to_last = -1
for i in range(n):
cost = dp[(1 << n) - 1][i]
for j in range(n):
if i != j:
cost += abs(i - nums[j])
if cost < min_cost:
min_cost = cost
start_node = j
second_to_last = i
cost -= abs(i - nums[j])
perm = [0] * n
perm[n - 1] = start_node
mask = (1 << n) - 1
current = second_to_last
for i in range(n - 2, -1, -1):
perm[i] = current
next_node = parent[mask][current]
mask ^= (1 << current)
current = next_node
lexicographically_smallest_perm = perm
return lexicographically_smallest_perm
Big O Runtime: O(n2 * 2n), where n is the length of the input array. The outer loops iterate through all masks (2n) and all possible pairs of last and next indices (n2). Backtracking to reconstruct the permutation takes O(n) time.
Big O Space: O(n * 2n), where n is the length of the input array. This is due to the space required to store the dp
table and the parent
table.
2 <= n
, so an empty input array is not a valid case.n <= 14
. The brute force solution will be very slow.