【周赛总结】第196场周赛——全一子矩阵,交换k次的最小整数

2020/07/05 未参赛,AC 3/4

第三题 全一子矩阵

【周赛总结】第196场周赛——全一子矩阵,交换k次的最小整数_第1张图片

思路参考最大矩形。
设计一个dp[i][j]表示以i,j为右下角的矩阵的最大值,我们需要枚举宽度和高度,但是我们可以通过预先设计一个矩阵,存储每个位置上方最大的连续矩阵1个数。

class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        ## 考虑时间复杂度降低为O(nnm)
        ## 定义dp[i][j]表示i,j这个点上方的连续1的个数
        n = len(mat)
        m = len(mat[0])
        dp = [[0]*m for _ in range(n)]
        for i in range(m):
            dp[0][i] = mat[0][i]
        for i in range(1,n):
            for j in range(m):
                if mat[i][j]:
                    dp[i][j] = dp[i-1][j]+1
        
        # 接下来计算矩形个数
        ans = 0
        # 前两层循环寻找右下角的点
        for i in range(n):
            for j in range(m):
                t = float('inf')
                for k in range(j,-1,-1):
                    t = min(t, dp[i][k])
                    ans += t
                    if t == 0:
                        break
        return ans

第四题:交换k次的最小整数

【周赛总结】第196场周赛——全一子矩阵,交换k次的最小整数_第2张图片

首先这道题目的一个核心在于贪心的使用,想要得到最小的数字,就需要尽量让较小的数字移到最高位。如果不足够让后面最小的数字移到最高位,也需要让尽可能小的数字移到最高位。

因此我们可以想象一个复杂度为 O ( n 2 ) O(n^2) O(n2)的算法,每次依次寻找一个k位以内的最小数字,然后移到最前端。这样可以递归的求解。

class Solution(object):
    def minInteger(self, num, k):
        if k <= 0 or not num: 
            return num
        
        # 这个是tricky的代码...不加会超时
        if len(num)**2//2 < k: 
            return ''.join(sorted(list(num)))

        # 每次寻找前`k+1`个数字中的最小数字的最小下标,复杂度O(n^2)
        index = num.index(min(num[:k+1]))
        res = num[index] + self.minInteger(num[:index] + num[index+1:], k - index)
        return res

一个比较好的算法是采用树状数组的方法求解,我们维护一个树状数组,数组中维护了每一个坐标移到最前面需要的步数。一般前面有数组的位置发生了改变,我们也可以很简单的维护数组中的区间和。

class Solution:
    def minInteger(self, num: str, k: int) -> str:
            # 树状数组做法
        def lowbit(x):
            return x & (-x)

        def add(index, val):
            while index < len(tree):
                tree[index] += val
                index += lowbit(index)

        def query(index):
            res = 0
            while index > 0:
                res += tree[index]
                index -= lowbit(index)
            return res

        n = len(num)
        if k >= n**2//2:
            return ''.join(sorted(num))
        tree = [0]*(n+1)
        ans = ""
        visited = set()
        dic = {}
        for i, c in enumerate(num):
            if c not in dic:
                dic[c] = collections.deque()
            dic[c].append(i)
        i = 0
        while k > 0 and i < n:
            for digit in '0123456789':
                if digit not in dic or not dic[digit]:
                    continue
                index = dic[digit][0]
                # query(index)维护了,index这个位置前面缺少的元素对移动带来的影响。
                need_step =index - query(index)
                if need_step <= k:
                    k -= need_step
                    ans += digit
                    add(index+1, 1)
                    dic[digit].popleft()
                    visited.add(index)
                    break
            i += 1
        for i in range(n):
            if i not in visited:
                ans += num[i]
        return ans

你可能感兴趣的:(周赛总结)