Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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.
Example 1:
Input: 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.
Example 2:
Input: 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 <= 14nums is a permutation of [0, 1, 2, ..., n - 1].Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Dynamic Programming · Bit Manipulation
[1,0,2]
[0,2,1]
shortest-path-visiting-all-nodes)find-the-shortest-superstring)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
class Solution {
private Integer[][] f;
private int[] nums;
private int[] ans;
private int n;
public int[] findPermutation(int[] nums) {
n = nums.length;
ans = new int[n];
this.nums = nums;
f = new Integer[1 << n][n];
g(1, 0, 0);
return ans;
}
private int dfs(int mask, int pre) {
if (mask == (1 << n) - 1) {
return Math.abs(pre - nums[0]);
}
if (f[mask][pre] != null) {
return f[mask][pre];
}
int res = Integer.MAX_VALUE;
for (int cur = 1; cur < n; ++cur) {
if ((mask >> cur & 1) == 0) {
res = Math.min(res, Math.abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur));
}
}
return f[mask][pre] = res;
}
private void g(int mask, int pre, int k) {
ans[k] = pre;
if (mask == (1 << n) - 1) {
return;
}
int res = dfs(mask, pre);
for (int cur = 1; cur < n; ++cur) {
if ((mask >> cur & 1) == 0) {
if (Math.abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur) == res) {
g(mask | 1 << cur, cur, k + 1);
break;
}
}
}
}
}
// Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
func findPermutation(nums []int) (ans []int) {
n := len(nums)
f := make([][]int, 1<<n)
for i := range f {
f[i] = make([]int, n)
for j := range f[i] {
f[i][j] = -1
}
}
var dfs func(int, int) int
dfs = func(mask, pre int) int {
if mask == 1<<n-1 {
return abs(pre - nums[0])
}
if f[mask][pre] != -1 {
return f[mask][pre]
}
res := &f[mask][pre]
*res = math.MaxInt32
for cur := 1; cur < n; cur++ {
if mask>>cur&1 == 0 {
*res = min(*res, abs(pre-nums[cur])+dfs(mask|1<<cur, cur))
}
}
return *res
}
var g func(int, int)
g = func(mask, pre int) {
ans = append(ans, pre)
if mask == 1<<n-1 {
return
}
res := dfs(mask, pre)
for cur := 1; cur < n; cur++ {
if mask>>cur&1 == 0 {
if abs(pre-nums[cur])+dfs(mask|1<<cur, cur) == res {
g(mask|1<<cur, cur)
break
}
}
}
}
g(1, 0)
return
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
# Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
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
// Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
// class Solution {
// private Integer[][] f;
// private int[] nums;
// private int[] ans;
// private int n;
//
// public int[] findPermutation(int[] nums) {
// n = nums.length;
// ans = new int[n];
// this.nums = nums;
// f = new Integer[1 << n][n];
// g(1, 0, 0);
// return ans;
// }
//
// private int dfs(int mask, int pre) {
// if (mask == (1 << n) - 1) {
// return Math.abs(pre - nums[0]);
// }
// if (f[mask][pre] != null) {
// return f[mask][pre];
// }
// int res = Integer.MAX_VALUE;
// for (int cur = 1; cur < n; ++cur) {
// if ((mask >> cur & 1) == 0) {
// res = Math.min(res, Math.abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur));
// }
// }
// return f[mask][pre] = res;
// }
//
// private void g(int mask, int pre, int k) {
// ans[k] = pre;
// if (mask == (1 << n) - 1) {
// return;
// }
// int res = dfs(mask, pre);
// for (int cur = 1; cur < n; ++cur) {
// if ((mask >> cur & 1) == 0) {
// if (Math.abs(pre - nums[cur]) + dfs(mask | 1 << cur, cur) == res) {
// g(mask | 1 << cur, cur, k + 1);
// break;
// }
// }
// }
// }
// }
// Accepted solution for LeetCode #3149: Find the Minimum Cost Array Permutation
function findPermutation(nums: number[]): number[] {
const n = nums.length;
const ans: number[] = [];
const f: number[][] = Array.from({ length: 1 << n }, () => Array(n).fill(-1));
const dfs = (mask: number, pre: number): number => {
if (mask === (1 << n) - 1) {
return Math.abs(pre - nums[0]);
}
if (f[mask][pre] !== -1) {
return f[mask][pre];
}
let res = Infinity;
for (let cur = 1; cur < n; ++cur) {
if (((mask >> cur) & 1) ^ 1) {
res = Math.min(res, Math.abs(pre - nums[cur]) + dfs(mask | (1 << cur), cur));
}
}
return (f[mask][pre] = res);
};
const g = (mask: number, pre: number) => {
ans.push(pre);
if (mask === (1 << n) - 1) {
return;
}
const res = dfs(mask, pre);
for (let cur = 1; cur < n; ++cur) {
if (((mask >> cur) & 1) ^ 1) {
if (Math.abs(pre - nums[cur]) + dfs(mask | (1 << cur), cur) === res) {
g(mask | (1 << cur), cur);
break;
}
}
}
};
g(1, 0);
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Pure recursion explores every possible choice at each step. With two choices per state (take or skip), the decision tree has 2ⁿ leaves. The recursion stack uses O(n) space. Many subproblems are recomputed exponentially many times.
Each cell in the DP table is computed exactly once from previously solved subproblems. The table dimensions determine both time and space. Look for the state variables — each unique combination of state values is one cell. Often a rolling array can reduce space by one dimension.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.