📜  最多 k 次交换后的最大排列(1)

📅  最后修改于: 2023-12-03 15:40:13.877000             🧑  作者: Mango

最多 k 次交换后的最大排列

题目描述:

给定一个长度为 n 的序列,将最多 k 次的交换操作应用于序列中的任意两个元素,得到一个新的序列。请问,在所有可能的新序列中,字典序最大的序列是哪一个?

输入格式:

  • 第一行包含两个整数 n,k,表示序列长度和最多交换次数,其中 1≤n≤10^5,0≤k≤10^9。
  • 第二行包含 n 个整数,表示给定的序列。

输出格式:

  • 输出一行包含 n 个整数,表示字典序最大的新序列。

示例:

输入:
3 1
3 1 2
输出:
3 2 1

算法一:并查集

采用并查集的思路,将相邻且可以交换的数合并在一起,交换两个数只会影响它们所在连通块的相对顺序,不会影响其它连通块内部的顺序。因此,问题转化为将每个连通块内的数按降序排序即可,排序时只需要记录每个位置是否已经被遍历过了。

时间复杂度 O(nlogn)。

python 代码如下:

from typing import List

class UnionFind:
    def __init__(self, n: int):
        self.parent = list(range(n))
        self.size = [1] * n
        
    def find(self, x: int) -> int:
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x: int, y: int) -> bool:
        x_parent, y_parent = self.find(x), self.find(y)
        if x_parent == y_parent:
            return False
        if self.size[x_parent] < self.size[y_parent]:
            x_parent, y_parent = y_parent, x_parent
        self.parent[y_parent] = x_parent
        self.size[x_parent] += self.size[y_parent]
        return True

class Solution:
    def maxPermutation(self, n: int, k: int, nums: List[int]) -> List[int]:
        uf = UnionFind(n)
        for i in range(n):
            uf.union(i, nums[i] - 1)
        pos = [[] for _ in range(n)]
        for i in range(n):
            pos[uf.find(i)].append(i)
        res = [0] * n
        for i in range(n):
            index = pos[uf.find(i)].pop()
            res[index] = n - i
        return res

算法二:堆

采用堆排序的思路,不断选择当前最大值,并将它与前面的数不断交换,直到它到达合适的位置或已经超过限制次数。交换时使用堆保存所有可以合法交换的数,取最大值保证当前字典序最大,离散化后数组可直接用树状数组维护。

时间复杂度 O(nlog^2n)。

python 代码如下:

from typing import List

class BinaryIndexTree:
    def __init__(self, n: int):
        self.sums = [0] * (n + 1)
    
    def lowbit(self, index: int) -> int:
        return index & (-index)
    
    def update(self, index: int, delta: int):
        while index < len(self.sums):
            self.sums[index] += delta
            index += self.lowbit(index)
    
    def query(self, index: int) -> int:
        res = 0
        while index > 0:
            res += self.sums[index]
            index -= self.lowbit(index)
        return res
        
class Solution:
    def maxPermutation(self, n: int, k: int, nums: List[int]) -> List[int]:
        pos = [0] * (n + 1)
        for i in range(n):
            pos[nums[i]] = i
        res = [0] * n
        bit = BinaryIndexTree(n)
        for i in range(n)[::-1]:
            if k == 0:
                res[i:] = nums[i:]
                break
            j = pos[n - i]
            if j == i:
                res[i] = nums[i]
                continue
            bit.update(j + 1, 1)
            cnt = bit.query(n) - bit.query(i)
            if cnt <= k:
                k -= cnt
                res[i:j+1] = nums[j:i-1:-1]
            else:
                bit.update(j + 1, -1)
                while k > 0:
                    mx_idx = 0
                    for t in range(i, j+1):
                        if bit.query(t) - bit.query(mx_idx) > k:
                            break
                        mx_idx = t
                    res[mx_idx], res[mx_idx-1] = res[mx_idx-1], res[mx_idx]
                    bit.update(mx_idx, -1)
                    k -= 1
                res[i:j+1] = nums[j:i-1:-1]
        return res