100312. 找出分数最低的排列
给你一个数组 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]
的一个排列。原站题解
golang 解法, 执行用时: 92 ms, 内存消耗: 8.6 MB, 提交时间: 2024-05-13 14:53:08
func findPermutation(a []int) []int { n := len(a) memo := make([][]int, 1<<n) for i := range memo { memo[i] = make([]int, n) for j := range memo[i] { memo[i][j] = -1 // -1 表示没有计算过 } } var dfs func(int, int) int dfs = func(s, j int) int { if s == 1<<n-1 { return abs(j - a[0]) } p := &memo[s][j] if *p != -1 { // 之前计算过 return *p } res := math.MaxInt for k := 1; k < n; k++ { if s>>k&1 == 0 { // k 之前没填过 res = min(res, dfs(s|1<<k, k)+abs(j-a[k])) } } *p = res // 记忆化 return res } ans := make([]int, 0, n) var makeAns func(int, int) makeAns = func(s, j int) { ans = append(ans, j) if s == 1<<n-1 { return } finalRes := dfs(s, j) for k := 1; k < n; k++ { if s>>k&1 == 0 && dfs(s|1<<k, k)+abs(j-a[k]) == finalRes { makeAns(s|1<<k, k) break } } } makeAns(1, 0) return ans } func abs(x int) int { if x < 0 { return -x }; return x }
golang 解法, 执行用时: 71 ms, 内存消耗: 41.4 MB, 提交时间: 2024-05-13 14:52:58
func findPermutation(a []int) []int { n := len(a) u := 1<<n - 1 f := make([][]int, 1<<n) g := make([][]int, 1<<n) for i := range f { f[i] = make([]int, n) for j := range f[i] { f[i][j] = math.MaxInt } g[i] = make([]int, n) } for j := range f[u] { f[u][j] = abs(j - a[0]) g[u][j] = -1 } for s := u - 2; s > 0; s -= 2 { // 注意偶数不含 0,是无效状态 for _s := uint(s); _s > 0; _s &= _s - 1 { j := bits.TrailingZeros(_s) for cus, lb := u^s, 0; cus > 0; cus ^= lb { lb = cus & -cus k := bits.TrailingZeros(uint(lb)) v := f[s|lb][k] + abs(j-a[k]) if v < f[s][j] { f[s][j] = v g[s][j] = k } } } } ans := make([]int, 0, n) for s, j := 0, 0; j >= 0; { ans = append(ans, j) s |= 1 << j j = g[s][j] } return ans } func abs(x int) int { if x < 0 { return -x }; return x }
golang 解法, 执行用时: 117 ms, 内存消耗: 12.2 MB, 提交时间: 2024-05-13 14:52:36
func findPermutation(a []int) []int { n := len(a) u := 1<<n - 1 f := make([][]int, 1<<n) g := make([][]int, 1<<n) for i := range f { f[i] = make([]int, n) for j := range f[i] { f[i][j] = math.MaxInt } g[i] = make([]int, n) } for j := range f[u] { f[u][j] = abs(j - a[0]) g[u][j] = -1 } for s := u - 2; s > 0; s -= 2 { // 注意偶数不含 0,是无效状态 for j := 0; j < n; j++ { if s>>j&1 == 0 { // 无效状态,因为 j 一定在 s 中 continue } for k := 1; k < n; k++ { if s>>k&1 > 0 { // k 之前填过 continue } v := f[s|1<<k][k] + abs(j-a[k]) if v < f[s][j] { f[s][j] = v g[s][j] = k // 记录该状态下填了哪个数 } } } } ans := make([]int, 0, n) for s, j := 0, 0; j >= 0; { ans = append(ans, j) s |= 1 << j j = g[s][j] } return ans } func abs(x int) int { if x < 0 { return -x }; return x }
java 解法, 执行用时: 109 ms, 内存消耗: 50.6 MB, 提交时间: 2024-05-13 14:52:14
class Solution { public int[] findPermutation(int[] a) { int n = a.length; int[][] f = new int[1 << n][n]; int[][] g = new int[1 << n][n]; for (int[] row : f) { Arrays.fill(row, Integer.MAX_VALUE); } for (int j = 0; j < n; j++) { f[(1 << n) - 1][j] = Math.abs(j - a[0]); } Arrays.fill(g[(1 << n) - 1], -1); for (int s = (1 << n) - 3; s > 0; s -= 2) { // 注意偶数不含 0,是无效状态 for (int j = 0; j < n; j++) { if ((s >> j & 1) == 0) { // 无效状态,因为 j 一定在 s 中 continue; } for (int k = 1; k < n; k++) { if ((s >> k & 1) > 0) { // k 之前填过 continue; } int v = f[s | 1 << k][k] + Math.abs(j - a[k]); if (v < f[s][j]) { f[s][j] = v; g[s][j] = k; // 记录该状态下填了哪个数 } } } } int[] ans = new int[n]; int s = 0, i = 0, j = 0; while (j >= 0) { ans[i++] = j; s |= 1 << j; j = g[s][j]; } return ans; } }
java 解法, 执行用时: 92 ms, 内存消耗: 45.5 MB, 提交时间: 2024-05-13 14:52:03
class Solution { public int[] findPermutation(int[] a) { int n = a.length; int[][] memo = new int[1 << n][n]; for (int[] row : memo) { Arrays.fill(row, -1); // -1 表示没有计算过 } int[] ans = new int[n]; makeAns(1, 0, a, memo, ans, 0); return ans; } private int dfs(int s, int j, int[] a, int[][] memo) { if (s == (1 << a.length) - 1) { return Math.abs(j - a[0]); } if (memo[s][j] != -1) { // 之前计算过 return memo[s][j]; } int res = Integer.MAX_VALUE; for (int k = 1; k < a.length; k++) { if ((s >> k & 1) == 0) { // k 之前没填过 res = Math.min(res, dfs(s | 1 << k, k, a, memo) + Math.abs(j - a[k])); } } memo[s][j] = res; // 记忆化 return res; } private void makeAns(int s, int j, int[] a, int[][] memo, int[] ans, int i) { ans[i] = j; if (s == (1 << a.length) - 1) { return; } int finalRes = dfs(s, j, a, memo); for (int k = 1; k < a.length; k++) { if ((s >> k & 1) == 0 && dfs(s | 1 << k, k, a, memo) + Math.abs(j - a[k]) == finalRes) { makeAns(s | 1 << k, k, a, memo, ans, i + 1); break; } } } }
cpp 解法, 执行用时: 291 ms, 内存消耗: 121.9 MB, 提交时间: 2024-05-13 14:51:44
class Solution { public: vector<int> findPermutation(vector<int>& a) { int n = a.size(); vector<vector<int>> f(1 << n, vector<int>(n, INT_MAX)); vector<vector<int>> g(1 << n, vector<int>(n, -1)); for (int j = 0; j < n; j++) { f[(1 << n) - 1][j] = abs(j - a[0]); } for (int s = (1 << n) - 3; s > 0; s -= 2) { // 注意偶数不含 0,是无效状态 for (int j = 0; j < n; j++) { if ((s >> j & 1) == 0) { // 无效状态,因为 j 一定在 s 中 continue; } for (int k = 1; k < n; k++) { if (s >> k & 1) { // k 之前填过 continue; } int v = f[s | 1 << k][k] + abs(j - a[k]); if (v < f[s][j]) { f[s][j] = v; g[s][j] = k; // 记录该状态下填了哪个数 } } } } vector<int> ans; int s = 0, j = 0; while (j >= 0) { ans.push_back(j); s |= 1 << j; j = g[s][j]; } return ans; } // 记忆化搜索 vector<int> findPermutation2(vector<int>& a) { int n = a.size(); vector<vector<int>> memo(1 << n, vector<int>(n, -1)); // -1 表示没有计算过 function<int(int, int)> dfs = [&](int s, int j) -> int { if (s == (1 << n) - 1) { return abs(j - a[0]); } int& res = memo[s][j]; // 注意这里是引用 if (res != -1) { // 之前计算过 return res; } res = INT_MAX; for (int k = 1; k < n; k++) { if ((s >> k & 1) == 0) { // k 之前没填过 res = min(res, dfs(s | 1 << k, k) + abs(j - a[k])); } } return res; }; vector<int> ans; function<void(int, int)> make_ans = [&](int s, int j) -> void { ans.push_back(j); if (s == (1 << n) - 1) { return; } int final_res = dfs(s, j); for (int k = 1; k < n; k++) { if ((s >> k & 1) == 0 && dfs(s | 1 << k, k) + abs(j - a[k]) == final_res) { make_ans(s | 1 << k, k); break; } } }; make_ans(1, 0); return ans; } };
python3 解法, 执行用时: 2271 ms, 内存消耗: 143.2 MB, 提交时间: 2024-05-13 14:50:59
class Solution: def findPermutation(self, a: List[int]) -> List[int]: n = len(a) @cache # 缓存装饰器,避免重复计算 dfs 的结果(记忆化) def dfs(s: int, j: int) -> int: if s == (1 << n) - 1: # 所有位置都填完了,最后一个位置是下标 j return abs(j - a[0]) res = inf # 枚举当前位置填下标 k for k in range(1, n): if s >> k & 1 == 0: # k 之前没填过 res = min(res, dfs(s | 1 << k, k) + abs(j - a[k])) return res ans = [] # 原理见上面贴的题解链接 def make_ans(s: int, j: int) -> None: ans.append(j) if s == (1 << n) - 1: return final_res = dfs(s, j) for k in range(1, n): if s >> k & 1 == 0 and dfs(s | 1 << k, k) + abs(j - a[k]) == final_res: make_ans(s | 1 << k, k) break make_ans(1, 0) return ans # 递推 def findPermutation2(self, a: List[int]) -> List[int]: n = len(a) f = [[inf] * n for _ in range(1 << n)] g = [[-1] * n for _ in range(1 << n)] for j in range(n): f[-1][j] = abs(j - a[0]) for s in range((1 << n) - 3, 0, -2): # 注意偶数不含 0,是无效状态 for j in range(n): if s >> j & 1 == 0: # 无效状态,因为 j 一定在 s 中 continue for k in range(1, n): if s >> k & 1: # k 之前填过 continue v = f[s | 1 << k][k] + abs(j - a[k]) if v < f[s][j]: f[s][j] = v g[s][j] = k # 记录该状态下填了哪个数 ans = [] s = j = 0 while j >= 0: ans.append(j) s |= 1 << j j = g[s][j] return ans