列表

详情


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

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: vector<int> findPermutation(vector<int>& nums) { } };

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

上一题